Git
Chapters ▾ 2nd Edition

7.13 Orodja Git - Zamenjava

Zamenjava

Kot smo poudarili že prej, objekti v objektni podatkovni bazi Git niso spremenljivi, vendar Git ponuja zanimiv način pretvarjanja zamenjave objektov v svoji podatkovni bazi z drugimi objekti.

Ukaz replace vam omogoča določiti objekt v Gitu in povedati »vsakič, ko se sklicuješ na ta objekt, se pretvarjaj, da je drugačen objekt«. To je najpogostejše uporabno za zamenjavo ene potrditve v vaši zgodovini z drugo, brez da morate ponovno zgraditi celo zgodovino z recimo git filter-branch.

Na primer, predpostavimo, da imate veliko zgodovino kode in želite razdeliti svoj repozitorij v eno hitro zgodovino za nove razvijalce in eno veliko daljšo in večjo zgodovino za ljudi, ki jih zanima rudarjenje podatkov. Presadite lahko eno zgodovino v drugo z zamenjavo najnovejše potrditve v novi vrstici z najnovejšo potrditvijo na stari. To je dobro, ker pomeni, da vam ni treba dejansko prepisati vsake potrditve v novi zgodovini, kot bi običajno morali, da ju združite skupaj (ker nadrejenost vpliva na SHA-1).

Poskusimo to. Vzemimo obstoječi repozitorij, ga razdelimo v dva repozitorija, enega novejšega in enega zgodovinskega, ter nato bomo videli, kako lahko ponovno kombiniramo oba brez sprememb zadnjih vrednosti SHA-1 repozitorija preko replace.

Uporabili bomo enostaven repozitorij s petimi enostavnimi potrditvami:

$ git log --oneline
ef989d8 Fifth commit
c6e1e95 Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit

To želimo prelomiti v dve liniji zgodovine. Ena linija gre iz prve potrditve v četrto potrditev — ta bo zgodovinska. Druga linija bo vsebovala samo potrditvi štiri in pet — to bo novejša zgodovina.

Primer zgodovine Git
Slika 163. Primer zgodovine Git

Torej, ustvarjanje zgodovinske zgodovine je enostavno, lahko damo zgolj vejo v zgodovino in nato potisnemo to vejo v vejo master novega oddaljenega repozitorija.

$ git branch history c6e1e95
$ git log --oneline --decorate
ef989d8 (HEAD, master) Fifth commit
c6e1e95 (history) Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
Ustvarjanje nove veje `history`
Slika 164. Ustvarjanje nove veje history

Sedaj lahko potisnemo novo vejo history v vejo master našega novega repozitorija:

$ git remote add project-history https://github.com/schacon/project-history
$ git push project-history history:master
Counting objects: 12, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (12/12), 907 bytes, done.
Total 12 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (12/12), done.
To git@github.com:schacon/project-history.git
 * [new branch]      history -> master

Dobro, sedaj je torej naša zgodovina objavljena. Sedaj je težji del skrajšanje naše novejše zgodovine navzdol, da je manjša. Narediti moramo prekrivanje, da lahko zamenjamo potrditev v eni enakovredni potrditvi v drugo, torej bomo skrajšali to na samo potrditvi štiri in pet (torej se potrditev štiri prekrije).

$ git log --oneline --decorate
ef989d8 (HEAD, master) Fifth commit
c6e1e95 (history) Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit

V tem primeru je uporabno ustvariti osnovno potrditev, da ima navodila, kako razširiti zgodovino, tako da drugi razvijalci vejo, kaj narediti, če pridejo do prve potrditve v skrajšani zgodovini in morajo imeti več. Torej, kar bomo naredili, je ustvarjanje začetnega objekta potrditve kot naše osnovne točke z navodili, nato pa bomo ponovno bazirali ostali potrditvi (štiri in pet) na vrhu le te.

Da to naredimo, moramo izbrati točko za razdelitev, ki bo za nas tretja potrditev, ki je 9c68fdc v jeziku SHA. Torej naša osnovna potrditev bo osnovana na tem drevesu. Ustvarimo lahko našo osnovno potrditev z uporabo ukaza commit-tree, ki samo vzame drevo in nam vrne popolnoma nov objekt potrditve SHA-1 brez nadrejene.

$ echo 'Get history from blah blah blah' | git commit-tree 9c68fdc^{tree}
622e88e9cbfbacfb75b5279245b9fb38dfea10cf
Opomba

Ukaz commit-tree je eden izmed skupkov ukazov, ki se običajno imenujejo kot ukazi »napeljave« (angl. plumbing). Ti ukazi v splošnem niso mišljeni za neposredno uporabo, vendar so namesto tega uporabljeni pri drugih ukazih Git, da opravljajo manjše naloge. Občasno, ko delamo bolj čudne stvari kot to, nam omogočajo narediti resnično nižje nivojske stvari, vendar niso mišljeni za vsakodnevno uporabo. Več o ukazih napeljave lahko preberete v Napeljava in keramika.

Ustvarjanje osnovne potrditve z uporabo `commit-tree`
Slika 165. Ustvarjanje osnovne potrditve z uporabo commit-tree

Torej sedaj, ko imamo osnovno potrditev, lahko naredimo ponovno baziranje na naši preostali zgodovini na vrhu tega z git rebase --onto. Argument --onto bo SHA-1, ki smo ga ravno dobili iz commit-tree, in točka ponovnega baziranja bo tretja potrditev (nadrejena prve potrditve, ki jo želimo obdržati, 9c68fdc):

$ git rebase --onto 622e88 9c68fdc
First, rewinding head to replay your work on top of it...
Applying: fourth commit
Applying: fifth commit
Ponovno baziranje zgodovine na vrh osnovne potrditve
Slika 166. Ponovno baziranje zgodovine na vrh osnovne potrditve

Torej sedaj smo prepisali svojo novejšo zgodovino na vrhu ovržene osnovne potrditve, ki ima sedaj v njej navodila, kako rekonstruirati celotno zgodovino, če to želimo. Potisnemo lahko to novo zgodovino v nov projekt in sedaj, ko bodo ljudje klonirali ta repozitorij, bodo videli samo zadnji dve potrditvi in nato osnovno potrditev z navodili.

Sedaj preklopimo vloge nekomu, ki prvič klonira projekt, in želi celotno zgodovino. Da dobimo podatke zgodovine po kloniranju tega skrajšanega repozitorija, bi nekdo moral dodati drugo daljavo za zgodovinski repozitorij in jo prenesti:

$ git clone https://github.com/schacon/project
$ cd project

$ git log --oneline master
e146b5f Fifth commit
81a708d Fourth commit
622e88e Get history from blah blah blah

$ git remote add project-history https://github.com/schacon/project-history
$ git fetch project-history
From https://github.com/schacon/project-history
 * [new branch]      master     -> project-history/master

Sedaj bi sodelavec moral imeti svoje zadnje potrditve v veji master in zgodovinske potrditve v veji project-history/master.

$ git log --oneline master
e146b5f Fifth commit
81a708d Fourth commit
622e88e Get history from blah blah blah

$ git log --oneline project-history/master
c6e1e95 Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit

Da jih kombinirate, lahko enostavno pokličete git replace s potrditvijo, ki jo želite zamenjati, in nato dodate potrditev, ki jo želite z njo zamenjati. Torej želimo zamenjati četrto potrditev v veji master s četrto potrditvijo v veji project-history/master:

$ git replace 81a708d c6e1e95

Če sedaj pogledate zgodovino veje master, je videti nekako takole:

$ git log --oneline master
e146b5f Fifth commit
81a708d Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit

Odlično, kajne? Brez da moramo spremeniti vse SHA-1 povratnega toka, smo lahko zamenjali eno potrditev v naši zgodovini s popolnoma drugo potrditvijo in vsa običajna orodja (bisect, blame itd) bodo delovala, kakor pričakujemo.

Kombiniranje potrditev z `git replace`
Slika 167. Kombiniranje potrditev z git replace

Zanimivo je, da še vedno kaže 81a708d kot SHA-1, čeprav dejansko uporablja podatke potrditve c6e1e95, s katero smo jo zamenjali. Tudi če poženete ukaz, kot je cat-file, vam bo pokazal zamenjane podatke:

$ git cat-file -p 81a708d
tree 7bc544cf438903b65ca9104a1e30345eee6c083d
parent 9c68fdceee073230f19ebb8b5e7fc71b479c0252
author Scott Chacon <schacon@gmail.com> 1268712581 -0700
committer Scott Chacon <schacon@gmail.com> 1268712581 -0700

fourth commit

Zapomniti si je dobro, da je bila dejansko nadrejena 81a708d označba mesta potrditve (622e88e) in ne 9c68fdce, kakor je tu zabeleženo.

Druga zanimiva stvar je, da so ti podatki vključeni v naših referencah:

$ git for-each-ref
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit	refs/heads/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit	refs/remotes/history/master
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit	refs/remotes/origin/HEAD
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit	refs/remotes/origin/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit	refs/replace/81a708dd0e167a3f691541c7a6463343bc457040

To pomeni, da je našo zamenjavo enostavno deliti z drugimi, ker lahko potisnemo to na svoj strežnik in drugi ljudje lahko to enostavno prenesejo. To ni toliko koristno v primeru presajanja zgodovine, kot smo šli skozi tu (ker bi vsak prenesel obe zgodovini tako ali tako, torej zakaj ju sploh ločevati?), vendar je lahko uporabno v drugih okoliščinah.

scroll-to-top