Chapters ▾ 2nd Edition

7.13 Git Tools - Vervang (Replace)

Vervang (Replace)

Soos ons vroeër beklemtoon het, is die objekte in Git se objekdatabasis onveranderlik, maar Git bied wel 'n interessante manier om te maak asof dit objekte in sy databasis met ander objekte vervang.

Die replace opdrag laat jou toe om 'n objek in Git te spesifiseer en te sê "elke keer as jy na hierdie objek verwys, maak asof dit 'n ander objek is". Dit is die nuttigste om een vaslegging (commit) in jou geskiedenis met 'n ander te vervang sonder om die hele geskiedenis te hoef te herbou met, sê maar, git filter-branch.

Byvoorbeeld, sê nou jy het 'n massiewe kodegeskiedenis en wil jou bewaarplek (repository) opsplits in een kort geskiedenis vir nuwe ontwikkelaars en een baie langer en groter geskiedenis vir mense wat belangstel in data-ontginning (data mining). Jy kan die een geskiedenis op die ander ent (graft) deur die vroegste vaslegging in die nuwe lyn te "vervang" met die nuutste vaslegging in die oue. Dit is lekker omdat dit beteken dat jy nie eintlik elke vaslegging in die nuwe geskiedenis hoef te herskryf nie, soos wat jy normaalweg sou moes doen om hulle aan mekaar te koppel (omdat die afkoms die SHA-1’s beïnvloed).

Kom ons probeer dit uit. Kom ons neem 'n bestaande bewaarplek, deel dit op in twee bewaarplekke, een resente en een historiese, en dan sal ons sien hoe ons hulle kan herkombineer sonder om die resente bewaarplek se SHA-1-waardes via replace te wysig.

Ons sal 'n eenvoudige bewaarplek met vyf eenvoudige vasleggings gebruik:

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

Ons wil dit opbreek in twee lyne van geskiedenis. Een lyn loop van vaslegging een tot vaslegging vier - dit sal die historiese een wees. Die tweede lyn sal net vasleggings vier en vyf wees - dit sal die resente geskiedenis wees.

Example Git history
Figure 176. Voorbeeld van Git-geskiedenis

Wel, om die historiese geskiedenis te skep is maklik; ons kan net 'n tak in die geskiedenis plaas en dan daardie tak na die master tak van 'n nuwe afgeleë bewaarplek (remote repository) push.

$ 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
Creating a new `history` branch
Figure 177. Skep van 'n nuwe history tak

Nou kan ons die nuwe history tak na die master tak van ons nuwe bewaarplek push:

$ 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

Oukei, so ons geskiedenis is gepubliseer. Nou is die moeiliker deel om ons resente geskiedenis af te knot (truncate) sodat dit kleiner is. Ons het 'n oorvleueling nodig sodat ons 'n vaslegging in die een kan vervang met 'n ekwivalente vaslegging in die ander, so ons gaan dit afknot na net vasleggings vier en vyf (so vaslegging vier oorvleuel).

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

Dit is in hierdie geval nuttig om 'n basisvaslegging (base commit) te skep wat instruksies het oor hoe om die geskiedenis uit te brei, sodat ander ontwikkelaars weet wat om te doen as hulle die eerste vaslegging in die afgeknotte geskiedenis tref en meer nodig het. So, wat ons gaan doen, is om 'n aanvanklike vasleggingsobjek as ons basispunt met instruksies te skep, en dan die oorblywende vasleggings (vier en vyf) bo-op dit te rebase.

Om dit te doen, moet ons 'n punt kies om af te splits, wat vir ons die derde vaslegging is, wat 9c68fdc in SHA-taal is. Dus sal ons basisvaslegging op daardie boom (tree) gebaseer wees. Ons kan ons basisvaslegging skep met behulp van die commit-tree opdrag, wat net 'n boom neem en vir ons 'n splinternuwe, ouerlose vasleggingsobjek SHA-1 teruggee.

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

Die commit-tree opdrag is een van 'n stel opdragte waarna algemeen as 'loodgieterswerk' (plumbing) opdragte verwys word. Hierdie is opdragte wat oor die algemeen nie bedoel is om direk gebruik te word nie, maar in plaas daarvan deur ander Git-opdragte gebruik word om kleiner take te verrig. By geleenthede wanneer ons vreemder dinge soos hierdie doen, laat hulle ons toe om regtig laevlak dinge te doen, maar dit is nie vir daaglikse gebruik bedoel nie. Jy kan meer oor loodgietersopdragte lees in Loodgieterswerk en Porselein (Plumbing and Porcelain).

Creating a base commit using `commit-tree`
Figure 178. Skep van 'n basisvaslegging met commit-tree

Goed, aangesien ons nou 'n basisvaslegging het, kan ons die res van ons geskiedenis bo-op dit rebase met git rebase --onto. Die --onto argument sal die SHA-1 wees wat ons pas van commit-tree af teruggekry het en die rebase-punt sal die derde vaslegging wees (die ouer van die eerste vaslegging wat ons wil behou, 9c68fdc):

$ git rebase --onto 622e88 9c68fdc
First, rewinding head to replay your work on top of it...
Applying: fourth commit
Applying: fifth commit
Rebasing the history on top of the base commit
Figure 179. Herbasering (Rebasing) van die geskiedenis bo-op die basisvaslegging

Goed, ons het nou ons resente geskiedenis herskryf bo-op 'n weggooi-basisvaslegging wat nou instruksies in het oor hoe om die hele geskiedenis te hersaamstel as ons wou. Ons kan daardie nuwe geskiedenis na 'n nuwe projek push en nou as mense daardie bewaarplek kloon, sal hulle net die mees onlangse twee vasleggings sien en dan 'n basisvaslegging met instruksies.

Kom ons ruil nou rolle om na iemand wat die projek vir die eerste keer kloon en wat die hele geskiedenis wil hê. Om die geskiedenisdata te kry na die kloning van hierdie afgeknotte bewaarplek, sal mens 'n tweede remote vir die historiese bewaarplek moet byvoeg en afhaal (fetch):

$ 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

Nou sal die medewerker (collaborator) hul resente vasleggings in die master tak hê en die historiese vasleggings in die project-history/master tak.

$ 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

Om hulle te kombineer, kan jy eenvoudig git replace aanroep met die vaslegging wat jy wil vervang en dan die vaslegging waarmee jy dit wil vervang. Ons wil dus die "vierde" vaslegging in die master tak vervang met die "vierde" vaslegging in die project-history/master tak:

$ git replace 81a708d c6e1e95

As jy nou na die geskiedenis van die master tak kyk, blyk dit om so te lyk:

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

Koel, nê? Sonder om al die SHA-1’s stroomop te hoef verander, kon ons een vaslegging in ons geskiedenis vervang met 'n heeltemal ander vaslegging en al die normale gereedskap (bisect, blame, ens.) sal werk soos ons sou verwag.

Combining the commits with `git replace`
Figure 180. Kombinering van die vasleggings met git replace

Interessant genoeg wys dit steeds 81a708d as die SHA-1, alhoewel dit eintlik die c6e1e95 vasleggingsdata gebruik waarmee ons dit vervang het. Selfs al voer jy 'n opdrag soos cat-file uit, sal dit vir jou die vervangde data wys:

$ 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

Onthou dat die werklike ouer van 81a708d ons plekhouer-vaslegging (placeholder commit - 622e88e) was, nie 9c68fdce soos dit hier aandui nie.

Nog 'n interessante ding is dat hierdie data in ons verwysings (references) gehou word:

$ 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

Dit beteken dat dit maklik is om ons vervanging met ander te deel, want ons kan dit na ons bediener push en ander mense kan dit maklik aflaai. Dit is nie so nuttig in die geskiedenis-entings-scenario (history grafting scenario) waaroor ons hier gegaan het nie (aangesien almal in elk geval albei geskiedenisse sou aflaai, so hoekom hulle skei?), maar dit kan nuttig wees in ander omstandighede.