Git Pull Force - Ako prepísať lokálne zmeny pomocou Git

Keď sa naučíte kódovať, skôr či neskôr sa dozviete aj o Systémoch riadenia verzií. A hoci v tomto priestore existuje veľa konkurenčných nástrojov, jedným z nich je de facto štandard používaný takmer každým v odbore. Je tak populárny, že existujú spoločnosti, ktoré používajú tento názov pri svojej značke. Hovoríme samozrejme o Gite.

Aj keď je Git mocný nástroj, jeho sila je dobre skrytá. Existuje niekoľko základných pojmov, ktorým musíte porozumieť, aby ste s Gitom skutočne ovládali. Dobrá správa je, že akonáhle sa ich naučíte, sotva sa niekedy dostanete do problémov, z ktorých nemôžete uniknúť.

Typický pracovný tok

V typickom pracovnom postupe Git budete používať lokálne úložisko, vzdialené úložisko a jednu alebo viac pobočiek. Úložiská ukladajú všetky informácie o projekte vrátane celej jeho histórie a všetkých pobočiek. Pobočka je v podstate súbor zmien, ktoré vedú od prázdneho projektu k súčasnému stavu.

Po klonovaní úložiska pracujete na miestnej kópii a zavádzate nové zmeny. Kým nevytlačíte miestne zmeny do vzdialeného úložiska, všetka vaša práca je k dispozícii iba na vašom počítači.

Po dokončení úlohy je čas na synchronizáciu so vzdialeným úložiskom. Chcete stiahnuť zmeny na diaľku, aby ste držali krok s pokrokom projektu, a chcete presunúť miestne zmeny, aby ste zdieľali svoju prácu s ostatnými.

Miestne zmeny

Všetko je v poriadku, keď vy a zvyšok vášho tímu pracujete na úplne samostatných súboroch. Nech sa stane čokoľvek, nebudete si navzájom šliapať na nohy.

Sú však chvíle, kedy vy a vaši spoluhráči súčasne zavádzate zmeny na rovnakom mieste. A tu zvyčajne začínajú problémy.

Už ste niekedy popravovali, git pulllen aby ste videli obávaného error: Your local changes to the following files would be overwritten by merge:? Skôr alebo neskôr sa všetci stretnú s týmto problémom.

Mätúce je tu skôr to, že sa vám nechce nič spájať, iba ťahať, že? V skutočnosti je ťahanie trochu komplikovanejšie, ako by ste si mysleli.

Ako presne Git Pull funguje?

Pull nie je jediná operácia. Skladá sa z načítania údajov zo vzdialeného servera a následného zlúčenia zmien s lokálnym úložiskom. Tieto dve operácie je možné vykonať manuálne, ak chcete:

git fetch git merge origin/$CURRENT_BRANCH

Táto origin/$CURRENT_BRANCHčasť znamená, že:

  • Git zlúči zmeny zo vzdialeného úložiska s názvom origin(toho, z ktorého ste klonovali)
  • ktoré boli pridané do $CURRENT_BRANCH
  • ktoré sa vo vašej miestnej odhlásenej pobočke už nenachádzajú

Pretože Git vykonáva zlúčenie iba vtedy, keď neexistujú žiadne nezadané zmeny, pri každom spustení git pulls nezadanými zmenami by ste sa mohli dostať do problémov. Našťastie existujú spôsoby, ako sa z problémov dostať v jednom kuse!

Sme rodina

Rôzne prístupy

Ak máte nezáväzné miestne zmeny a stále chcete zo vzdialeného servera načítať novú verziu, váš prípad použitia zvyčajne spadá do jedného z nasledujúcich scenárov. Buď:

  • nestaráte sa o miestne zmeny a chcete ich prepísať,
  • na zmenách vám veľmi záleží a chceli by ste ich použiť po vzdialených zmenách,
  • chcete stiahnuť vzdialené úpravy, ale zatiaľ ich nepoužívať

Každý z prístupov si vyžaduje iné riešenie.

Nezaujímajú vás miestne zmeny

V takom prípade chcete iba zrušiť všetky miestne zmeny, ktoré neboli potvrdené. Možno ste upravili súbor na experimentovanie, ale už ho nepotrebujete. Všetko, na čom vám záleží, je mať aktuálny stav proti prúdu.

To znamená, že medzi načítaním vzdialených zmien a ich zlúčením pridáte ešte jeden krok. V tomto kroku sa vetva uvedie do neupraveného stavu, čo umožní git mergejej prácu.

git fetch git reset --hard HEAD git merge origin/$CURRENT_BRANCH

Ak si nechcete písať mene vetvy pri každom spustení tohto príkazu Git má pekný zástupcu smerujúce do upstream vetva: @{u}. Nadradená vetva je vetva vo vzdialenom úložisku, do ktorej tlačíte a z ktorej načítate.

Takto by vyzerali vyššie uvedené príkazy so skratkou:

git fetch git reset --hard HEAD git merge '@{u}'

Citujeme skratku v príklade, aby sme zabránili shellu v jeho interpretácii.

Veľmi vám záleží na miestnych zmenách

Ak sú pre vás vaše nezáväzné zmeny významné, existujú dve možnosti. Môžete ich spáchať a potom predviesť git pull, alebo ich môžete ukryť.

Ukrytie znamená odložiť zmeny na chvíľu, aby sa neskôr vrátili. Presnejšie, git stashvytvorí potvrdenie, ktoré nie je viditeľné vo vašej aktuálnej vetve, ale je stále dostupné v Gite.

Ak chcete vrátiť zmeny uložené v poslednej skrýši, použijete git stash poppríkaz. Po úspešnom použití skrytých zmien tento príkaz tiež odstráni skrytý príkaz, pretože už nie je potrebný.

Pracovný postup by potom mohol vyzerať takto:

git fetch git stash git merge '@{u}' git stash pop

V predvolenom nastavení sa zmeny v skrýši stanú fázovanými. Ak ich chcete prestaviť, použite príkaz git restore --staged(ak používate Git novší ako 2.25.0).

Len si chcete stiahnuť vzdialené zmeny

Posledný scenár sa trochu líši od tých predchádzajúcich. Povedzme, že ste uprostred veľmi chaotického refaktoringu. Strata ani ich odkladanie nie je možnosťou. Napriek tomu stále chcete mať k dispozícii vzdialené zmeny, aby ste git diffich mohli spustiť .

Ako ste už pravdepodobne zistili, sťahovanie vzdialených zmien nevyžaduje git pullvôbec! git fetchje toho dosť.

One thing to note is that by default, git fetch will only bring you changes from the current branch. To get all the changes from all the branches, use git fetch --all. And if you'd like to clean up some of the branches that no longer exist in the remote repository, git fetch --all --prune will do the cleaning up!

Some Automation

Have you heard of Git Config? It's a file where Git stores all of the user-configured settings. It resides in your home directory: either as ~/.gitconfig or ~/.config/git/config. You can edit it to add some custom aliases that will be understood as Git commands.

For example, to have a shortcut equivalent to git diff --cached (that shows the difference between the current branch and the staged files), you'd add the following section:

[alias] dc = diff --cached

After that, you can run git dc whenever you wish to review the changes. Going this way, we can set up a few aliases related to the previous use cases.

[alias] pull_force = !"git fetch --all; git reset --hard HEAD; git merge @{u}" pf = pull_force pull_stash = !"git fetch --all; git stash; git merge @{u}; git stash pop"

This way, running git pull_force will overwrite the local changes, while git pull_stash will preserve them.

The Other Git Pull Force

Curious minds may have already discovered that there is such a thing as git pull --force. However, this is a very different beast to what's presented in this article.

It may sound like something that would help us overwrite local changes. Instead, it lets us fetch the changes from one remote branch to a different local branch. git pull --force only modifies the behavior of the fetching part. It is therefore equivalent to git fetch --force.

Like git push, git fetch allows us to specify which local and remote branch do we want to operate on. git fetch origin/feature-1:my-feature will mean that the changes in the feature-1 branch from the remote repository will end up visible on the local branch my-feature. When such an operation modifies the existing history, it is not permitted by Git without an explicit --force parameter.

Just like git push --force allows overwriting remote branches, git fetch --force (or git pull --force) allows overwriting local branches. It is always used with source and destination branches mentioned as parameters. An alternative approach to overwriting local changes using git --pull force could be git pull --force "@{u}:HEAD".

Conclusion

Svet Gitu je obrovský. Tento článok sa zaoberal iba jedným z aspektov údržby úložiska: začlenením vzdialených zmien do miestneho úložiska. Aj tento každodenný scenár vyžadoval, aby sme sa hlbšie pozreli na vnútorné mechanizmy tohto nástroja na kontrolu verzie.

Naučenie sa skutočných prípadov použitia vám pomôže lepšie pochopiť, ako Git funguje pod kapotou. Vďaka tomu sa budete cítiť oprávnene, kedykoľvek sa dostanete do problémov. Robíme to občas všetci.