Git
Chapters ▾ 2nd Edition

7.13 Orodja Git - Replace

Replace

Objekti v Git-u niso spremenljivi, vendar ponuja zanimiv način pretvarjanja zamenjave objektov v njegovi podatkovni bazi z drugimi objekti.

Ukaz replace vam omogoča določiti objekt v Git-u in povedati "vsakič ko to vidite, se pretvarjaj, da gre za drugo stvar". To je najpogostejše uporabno za zamenjavo enega pošiljanja v vaši zgodovini z drugim.

Na primer, predpostavimo, da imate veliko zgodovino kode in želite cepiti vaš repozitorij v eno hitro zgodovino za nove razvijalce in enega z veliko daljšo in večjo zgodovino za ljudi, ki jih zanima kopanje podatkov. Transplatirate lahko eno zgodovino v drugo z zamenjavo (replace) prejšnjih pošiljanj v novi vrstici z najnovejšim pošiljanjem na stari. To je lepo, ker pomeni, da vam ni potrebno dejansko prepisati vsakega pošiljanja v novi zgodovini, kot bi običajno morali, da ju združite skupaj (ker starševstvo vpliva na SHA-1).

Poskusimo to. Naredimo obstoječi repozitorij, ga cepimo v dva repozitorija, enega zadnjega in enega zgodovinskega in nato bomo videli, kako lahko ponovno kombiniramo oba brez sprememb zadnjih SHA-1 vrednosti repozitorija preko replace.

Uporabili bomo enostaven repozitorij s petimi enostavnimi pošiljanji:

$ git log --oneline
ef989d8 fifth commit
c6e1e95 fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit

Želimo to prelomiti v dve vrstici zgodovine. Ena vrstica gre iz prvega pošiljanja v četro pošiljanje - ta bo zgodovinski. Druga vrstica bosta samo pošiljanja 4 in 5.

replace1

Torej ustvarjanje zgodovinske zgodovine je enostavno, lahko samo damo vejo v zgodovino in nato potisnemo to vejo v glavno vejo 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
replace2

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 naša zgodovina objavljena. Sedaj je težji del krajšanje naše zadnje zgodovine navzdol, da je manjša. Narediti moramo prekriavnje, da lahko zamenjamo pošiljanje v enem ekvivalentnem pošiljanju v drugem, torej bomo skrajšali to na samo pošiljanja 4 in 5 (torej se pošiljanje 4 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 pošiljanje, da ima navodila, kako razširiti zgodovino, da drugi razvijalci vejo, kaj narediti, če pridejo do prvega pošiljanja v prekriti zgodovini in potrebujejo več. Torej, kar bomo naredili je ustvarjanje začetnega objekta pošiljanja kot našo osnovno točko z navodili, nato naredili rebase ostalih pošiljanj (4 in 5) na vrhu le tega.

Da to naredimo, potrebujemo izbrati točko za cepitev, ki bo za nas tretje pošiljanje, ki je 9c68fdc v besedi SHA. Torej naše osnovno pošiljanje bo osnovano na tem drevesu. Lahko ustvarimo naše osnovno pošiljanje z uporabo ukaza commit-tree, ki samo vzame drevo in nam vrne popolnoma nov objekt pošiljanja SHA-1 brez starša.

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

Ukaz commit-tree je eden izmed skupkov ukazov, ki so pogosto sklicevani kot ukazi napeljave (plumbing). Ti ukazi v splošnem niso mišljeni za direktno uporabo, vendar namesto tega so uporabljeni za druge Git ukaze, da naredijo manjše naloge. Občasno, ko delamo čudnejše stvari kot to, nam omogočajo narediti resnično nižje nivojske stvari, vendar niso mišljeni za dnevno uporabo. Več lahko preberete o ukazih napeljave v Napeljava in porcelan

replace3

Torej sedaj ko imamo osnovno pošiljanje, lahko naredimo t.i. rebase 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čke rebase-a bo tretje pošiljanje (starš prvega pošiljanja, ki ga ž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
replace4

Torej sedaj smo prepisali našo zadnjo zgodovino na vrhu ovrženeega osnovnega pošiljanja, ki ima sedaj navodila v njem, kako rekonstruirati celotno zgodovino, če to želimo. Lahko pošljemo to novo zgodovino v nov projekt in sedaj, ko ljudje klonirajo ta repozitorij, bodo videli samo zadnji dve pošiljanji in nato osnovno pošiljanje 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 ujeti:

$ 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 svoja zadnja pošiljanja v veji master in zgodovinsko pošiljanje 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 kombiniramo, lahko enostavno pokličete git replace s pošiljanjem, ki ga želite zamenjati in nato poslati, kar želite z njim zamenjati. Torej želimo zamenjati četrto pošiljanje v veji master s četrtim pošiljanjem v veji project-history/master:

$ git replace 81a708d c6e1e95

Sedaj, če pogledate zgodovino veje master, izgleda nekako takole:

$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit

Kul, kajne? Brez, da moramo spremeniti vse SHA-1 zgornjega toka, smo lahko zamenjali eno pošiljanje v naši zgodovini s popolnoma drugim pošiljanjem in vsa običajna orodja (bisect, blame itd) bodo delovala kakor pričakujemo.

replace5

Zanimivo še vedno kaže 81a708d kot SHA-1, čeprav dejansko uporablja c6e1e95, podatke pošiljanja, s katerimi smo jih 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 bil dejanski starš 81a708d ograda pošiljanja (622e88e) in ne 9c68fdce kakor je tu zabeleženo.

Druga zanimiva stvar je, da so ti podatki obdržani v naši referenci:

$ 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 enostavno deliti našo zamenjavo z drugimi, ker lahko potisnemo to na naš strežnik in drugi ljudje lahko to enostavno prenesejo. To ni v pomoč v zgodovini scenarija presadka, ko smo šli skozi tu (ker bi vsak prenesel obe zgodovini tako ali tako, torej zakaj ju ločiti?), vendar je lahko uporabno v drugih okoliščinah.