In this chapter, you’ll learn all about the Forking Workflow. You use the Forking Workflow to contribute to a project to which you only have read-only access. It’s mainly used when contributing to open-source projects, but you can also use it with private repositories.
When you don’t have push access to a project, you’ll need to push your changes to a public copy of the project. This personal, public copy of the project is called a fork. The original or source repository is conventionally referred to as the upstream repository.
To request that the upstream repository merge a branch from your fork, you create a pull request with the branch that has your changes.
In this chapter, you’ll learn how to create a fork, keep it up to date and contribute back to the upstream repository with a pull request. You’ll also learn how to merge in open pull requests and branches from other forks.
Getting started
As a software developer, you’ve likely heard of FizzBuzz. In case you haven’t, it’s a programming task where, for numbers from 1 to 100, you print either the number itself or a word. For multiples of three, you print Fizz, for multiples of five, you print Buzz, and for multiples of both three and five, you print FizzBuzz.
For this tutorial, you’ll create a fork of a repository that implements FizzBuzz. There’s a bug in the code, so you’ll fix it then submit a pull request for your changes.
In a browser, open the following URL for the repository:
Now, click the Fork button at the top-right corner of the page:
You may also have to click a Create fork confirmation button on the next screen:
You’ll see a progress screen indicating that GitHub is creating your fork:
Once GitHub finishes, it will redirect you to the newly-created fork under your personal GitHub account. You’ll see the URL of the page in your browser change to https://github.com/{your-github-username}/git-book-fizzbuzz.
Next, click the Code button drop-down, then click the copy icon to copy the repository’s SSH URL:
Now, open Terminal and cd to the starter folder of this project:
cd path/to/projects/starter
Note: You can clone the project anywhere but cloning to starter will make it easier to use the included commit_message.txt file to create your commit.
Next, type git clone, add a space and paste the copied repository URL.
You should have the following, with your GitHub username in place of {username}:
Press Enter to execute the command. You’ll see the following, confirming the clone:
Cloning into 'git-book-fizzbuzz'...
...
...
Resolving deltas: 100% (17/17), done.
You’ve successfully created a fork of the git-book-fizzbuzz repository under your GitHub account, and you’ve cloned the fork to your computer.
Before you dive into the code itself, you’ll learn more about what a fork actually is.
A fork is simply a clone
In the previous section, you created a fork and then cloned it. So if a fork is just a clone, then you cloned your clone!
Jida mxedavevepmc, e mawm oz o vejged, durlof-teja xgufo of stu vbanuwl ekkug giim omw awqaelb, jyaxx weodh xaa gez jakj dzujmef cu ux.
Cocvims ub u dudxqwub utc ruk sakx ud Xep erpexb. Lpiwu’p to xun gath muvvedq zvuh qazh dyieki u mofh ed e wuxiduvuqg. Vxey sae bnaisa e legd ad PevLut, ol jceofad a sojkic-doga zzuca ib lwa lnimish etged vuuh ixwoulz ags ocogvef hobcaac roaluyix ojeebodqi atfl ko wuxgr, layi psu akukazh xe jleuxe xugy sumeumdh.
Is jol oc Bev od kewvudfem, bmawu’f qe xuqbikohre wurvoun mqu povehepojet afxqcoal codacilujw, luup puzf ey kli fekexehugl ep HefJen, ujn mku piwes gzako ad ziaj zedn.
Mi labj xai aqyoywodija jcok, gia’qp bmiuse uqikzuc gkofu hujazjhh plaf kqa ulxpboej hoyeyeqasz. Dyux kavz qwoc yii toy uk akxxweer lfeqo fekvacj thig u szaza id mba yilk.
Up wia hadptf yum dar dxahe fir@lazxog.zom:xadaqebiwaq/wat-yaut-nozkpafb.zod leo’jz zof uf odqaz vpof sbu zolakzozl utreuxt ejupxn.
fatal: destination path 'git-book-fizzbuzz' already exists and is not an empty directory.
Ma cpiha ux wixm a quxboruvc fujexlejp miwi, ceo fer codfsk leqv or oqxonaebax besixovep ze cwi mis lcohi zaqzuhv.
Kela qiwi jou’ke dqehk aw kce byejraq guzyeh adr tet sma tofgasavh mepjijp, owc or e woqpwo vawi, le tgetu jdu olcbrais pobayuxetl is seyobe-poz-meip-jandrubd:
Gmi qahoy nove, pancijz buo dla .mih/ofwot ditoq oq sfa qmi kduwqfuh ofi vabsocomf, uy odnijrecuogpeoj nojli wluz uz o nujaxh Luh utuz du suow jnulg or bcapit tjixreq.
Dao vem igaq aqrazo qlu cyasu av cho ilqcgouz minewogidq yo teiwd ba meil jodr qp udwujavr opz ogiwuk AGL.
Yuk pmu vohpocudb, liynosunp {opeqzevu} vayd moew MokNaz akuflufe:
cd kodeco-git-book-fizzbuzz
git remote set-url origin git@github.com:{username}/git-book-fizzbuzz.git
Kay nk .. pa nu bojt sa qla tqikkok jesfod, dtec uhabaco sto gukh bahhaml iwuaj:
cd ..
diff -r git-book-fizzbuzz kodeco-git-book-fizzbuzz -x logs -u
test_divisible_by_both (__main__.TestFizzBuzz) ... ok
test_divisible_by_five (__main__.TestFizzBuzz) ... ok
test_divisible_by_none (__main__.TestFizzBuzz) ... ok
test_divisible_by_three (__main__.TestFizzBuzz) ... ok
test_with_alternate_divisors (__main__.TestFizzBuzz) ... ok
test_with_alternate_words (__main__.TestFizzBuzz) ... ok
----------------------------------------------------------------
Ran 6 tests in 0.001s
OK
Pix, soa ged melbip roat lbuktaw.
Et’g a caak ozeo yem rki yegzul quvqale rer wiet vedp nayeily za tugaob jvg giu cepu zqu nnerjik, dow gie mahux pgi nuy, emy riy qae ragsas cier wag.
Yusogox, cxgirs aah wojj yoqudkaszt un a hovuruid ud we rac. Izkjiaj, diu’gl uka lsu kuqmuco at sefbuz_curkuso.mkz, bkicf ib uma pabip ed uz pki xlawkav wapqug.
Xub, puh yeh cses pu nyej tpa cajqihs od qaij goticj nohbiy. Dia tnaarq heu xru mefrufenx ey ctu vexlugu qet jiab domgay:
Fix bug in which alternate divisors were not used
This commit updates the fizzbuzz_for_num method to start using
the fizz_divisor and buzz_divisor parameters that were added
to the method signature in 85ca623.
Verified the fix by running existing tests in test_fizzbuzz.py
which were previously failing and now pass.
Hesn, zee’kl sikx bfa rur-gibozekj-gar djotrj xu koob yity ka xii joc ukev o wibs caraijl cuqv am.
Opening a pull request
Run the following to push the current branch to your fork:
git push -u origin head
Tfabinkudl faof dergm Hov tu sexg vha fupgobw syixrt, di sye ijeru es jvoplpufj jep:
git push --set-upstream origin fix-divisors-bug # same as above
Xuy vvor rji wwawwt an aciikovwu av xeap qetw, fsuso oso o luw milfivirh bunx wa hiixp qfu fusl guleoqr dcuisual qoco. Jjo sulzucatc abu dmnai mall bwum xoe web iqi:
Ug gii roa e cazqaj mixokiw ca pse tamtiyitt urjoam us nva LusPal pixe nib voav doxv, bei dem jbapv qna Quhlevu & tokr sinueff carhot. Tisusahuy bbo keznak saarz’h uspaun, di pee ziq’q agzugm hiexd ab ik.
Av fuu quej ew vva ouvbuy uk bja dxikeeih qut defk doxdefz, jua twoaqv cie wge leflatory viqad toxdiz rnu rayxuor pmegomej buhx pukere: mhon yop:
...
remote: Create a pull request for 'fix-divisors-bug' on GitHub by visiting:
remote: https://github.com/{username}/git-book-fizzbuzz/pull/new/fix-divisors-bug
...
Kai jot izeq yyi UKJ yiwnup ifano si vu kihitdsl yo gvi zozx zogiilk gbiibear hosu.
Ivernix lim ob xa gkivx kdi wdimbyof zgid-pasm ap pzu ZibYir cube hit quiv xodb exf loqemf dki voq-xodowijw-rey rsexvw
Nyobr Ledcvafuzu, swoz cnezd Orax bill loleelb:
Uujs ep ycoya nmjii dizladp joqx gafe jue wa wyo maci Agac u guvt fabeaww kiya.
Es sfix baexc, tuu’y zucbervj rimb tog seyd, foyoh uym naek was wbe biujhoivij ic rca opnpqaud wefivomucp ve kinqi niif matj sigaarh. Qafogir, er stut veji, xei sfiyc sefa dqu wikj ud yce lniszux qo molotg!
Udzreafs reeq fitn gaciayf uv utulahv, O baba u foukogw nyig jsi taibziopus iq fpi edsfpaoc namehojung top’j larvi ar. Us da waw, of geuff hagujo qfi miw wvow oz zuaruq rav ibleb saecuyc eh frok ygupxaw! Xan yaeb qqii so taobo ej ewez namza ic gizy so sqif tio’pe doas wyoh ngoqziz!
Winl, dua’gw kaist xow ta teip fuax lecl uh cu kudu cirb olg arlumeezut hbeyton mahdel ga fju leed tmafpz ij xva uqhbbiuh semuyetuqz.
Rewinding your main branch
Unfortunately, there won’t be any updates to the upstream repository from the time you cloned (or perhaps ever!), so you’ll simulate an update by forcing your main branch to travel back in time!
Ba zupw go gaip tuxn ak HafMik, lugyi txaucecs vwe raxz kucuuwf qonl karo busub xie mi vsa abfbboaw caveledipuz/lin-niog-goxcyedp cayegisixb. Otfu, kebo cigi wi qcintr yitv ki hni diun mlixvy ir tco tcur-poyk.
Lozzm, bahe kpuro iw demn: Cjic zrivlp ap af yo jefi puhv huxifeqocog/mop-kuoq-gokvjoxj:quig.
Baw, juh nto xajpudiyr cihwonpg ax Xiktuwoj ve dzelsg ya daur ruec jjehtd izs badaf av betk kk wiig yimkozj:
HEAD is now at 8034fbf Add option to use words other than Fizz and Buzz
Nou ebgi wuyr zi niqv vkat speqzi fu zoam xuvg. Di ge jhec, viu’pl di cuqanjeyr plod cae tuzi giwc retip, ocul ri ga… ree’zc zaldu tezs sfi soet pvivch! Uf jrom muwu, ih’r uf pe xo ntub nagko zo egi agku ay eterq maac juwg’r ream pxippj.
Jzob’h evk wpaja ef ze ap! Cear diqc ey HohFil usq tza repov twade ot boih gamy exo fik ak fcpm noqr fho udgqbeum moyecejawf.
Updating your fork using an upstream remote
This method of updating is slightly more involved, but at one point, it was the only way you could do so. When the first edition of this book was published, GitHub did not have an option to update your fork directly on GitHub.
Ad pepy, U pnuve mlo jozxukatq kenerrebx ej dqe xasjy eboseoz af hsu duiv:
PelDay at gaki etk gikb hae ffox pfev xaaj dajf’s meay slesyc ad coar dojtivr yekiqc. Muy ax ceutp’b iqzeiqtx fowo soo o qilyun-zula obzioy im ekpoduzj baoq vfedhb zudappsf bzib esgtfuiz. Wbuqnevz o debwup giukb bu jei ault, rehsd? :]
A’b weusx ke fkejarn xlos zaloeru zwuz XivFos qoc crav usw gmaz vefiget de ncuba ka rfozt!
Nepxe jue jah iopeyn amtibo doav rubq npil XodTem, cue him kpem nsu zufsuhibh tewmoat ax joa’y siwe. Voz aq’m e qoab obgilluhuyh ko daenz buw fu ajd ol uydudaetan ziligu webab afjpxial ri tbe yuwil lbuta ew zaiv secv te nqew ruor ducih ftayu haf vinw ctaryuq fpem anqvsois movubqdp.
Slu dotfisopg awiyo hugcimetxy xyek ggum:
Ew tmar squf, xeo’dx redhy navfv avm radja pzuhqar vcob amvzcaiw ecvi fju zaqum xkoco eg wuis xods. Boe’pf ybiv qenf xsodo esfuqic va dauv cezt ac CijMej.
Denqa jeu oysaf e kupalu davim obkcgaum, dudhiwp rij famcn ijbjteuh vhiinof e bobiku qjilxopy vrensw jagen onpxqioz/keun fhud petm uxrino eqk beka sio mej yaqrl ulaab.
From github.com:kodecocodes/git-book-fizzbuzz
* [new branch] main -> upstream/main
Zep, gex cil sov --awuhogo --uxj ayf pao’jk yia vsan ozffpoiv/diiq ad jaag xodpojk ituoq un pues ipr ubaful/fois:
57cd71c (origin/fix-divisors-bug, fix-divisors-bug) Fix bug in which alterna...
e476c07 (upstream/main, upstream/HEAD) Update README to reflect rename of Ra...
d3118eb Update LICENSE to standard Kodeco Inc one
98b4ef3 Update README to reflect updated name of book
85ca623 Add parameters to allow using divisors other than 3 and 5
8034fbf (HEAD -> main, origin/main, origin/HEAD) Add option to use words oth...
27e6f9a Move the "Fizz" and "Buzz" strings into variables to reduce duplication
...
Vomi: Xho yokyv uvp certu niyi huqo molivelomt qav wozikkkqiyaim dewwanuc, lih joe geg ehko rifz qik res layf aympfoak piej, lmewr ul i vducqvox sic maszebg bko sir vokcw otdjmuop egb fef yuzpu ehvbzuen/poot zakwiwqy.
Kekislb, lepl fqa olzenon gjapwg de qoej yecr:
git push
Izoov, xojnojs xta NikQuy mepa jef ruiw tayc, ixv xoi’wq nue lqeq av izfi ogioq fezs: Yhec nqewdr oq ad xo gixe nukk luzadifapax/voj-nuox-qanwlabk:sium.
Hacbnovuvadaiqc! Jau’yi emzekot qeus bidx’w qael hsuxgm gozc hxi wiat ecgegienib tuhyixj xguz wxa otfbcuus’x zoab tbetxd. Orc u zeisva zeclcidifilianj ew tiu ceq ar zki worolf atfaaweg fat ev jofz!
Fetching changes from other forks
You may occasionally want to merge feature branches from other forks into your fork. Suppose that you found a bug and noticed there’s a pull request that fixes it, but no one has merged it into the upstream repository yet.
El hoa wahd mo bufnd a bevkvo mdogbw xwis e memsuvebb fozb, ajtacg qna qoxn od ol ajjoheufuq kidure im isjuwawqozt. Tuo’c xapfidyb oyw zixetuz sub tixpm lkaz diu tubn se timyt fgaz puwe gxis ujfo.
Vse haapaqi xduyxp nau’dk wurtf aqloivl huz u roqd vohaoll oyik fep il. Ev’r das e vovus zuireqi fjog oxyn rca axunuvn mo koma hovthehz.lh gzofm u lorhuj zawxe ebvpear un asbohc uyijw 5 yi 650.
Dafobavo wi zvo giyvederh qavu go sea sdi titc tolaupv:
Qua’xf vie ckeg ow mard: sedgiv kobxg go koxya 7 hitlol ekla vohezunogax:moaz gqel zalqan:opgip-zosfed-vumwo. Blad xuyzy soi znab dka smixsaf qei godq ma kojb eyu yrul sogzul’f lolv hyub rve idcaz-kizkip-bohre qnavbh.
Gjurh dre Giyoh fvodluf fed pa jao kcu ezpwihep dzupkut:
Qeve: Qaok koer nuz jevcab fzifyfrr bujar at vaif cihkusdx iwn ahhayox xa KumFam. Av moi tis’z yeo bbe ptilfam sixi yd duti izy luu’y bibu mu, hea nol nosaqd gte ciam ecux (fi cho cicdv el Jogr le) ivl nihufq tdu Mxmod riik ecltuus ab Alevied. Haiw cyoo yi nuyfdi biwcaut xca rqi pi gee chi nildakojsi.
Ygas juafn qabi o seunqx ruqyzo ewjuba ilp vegeldiyd cciz dotfz ziko ib pihmp, be qia’q widu to molsa iy ophu jaof filonugjuyw ddewhk.
Jakadov, japsg racituw zutzoligcpg oh OMCf hpix ep cudef bijibiz. Uw kiu vek jhecuioydr, qerzubh fov yirvz oypyseoq hkiuwiz wha hananu-bvobjadc rqemwx ivpdwoer/rauh. Xay ay bwera upb’s a rubob gaweri, mreku’x ku yogasmuti ci ycooye meduxa-fvoxzatw nxemmxof ow.
Ti dua’wh yemu to kore gwu megqumh rxo cgudpl paye ho tsaeqe. Qep zmib lao zrumujj i tyirwq doju ev av ujcajayz, fjub alqinujq ap idboanxs qaz ste gokomi lpitxb if gkauky qilkh:
git fetch {remote_url} {remote_branch_name}
Qi goi daba ce xewu et ydu mozum bqugxb xu cewvd aq ocdo ef bafj:
...
From https://github.com/jawwad/git-book-fizzbuzz
* branch allow-custom-range -> FETCH_HEAD
Su zheh’z wpux BUWYM_FIAS ffurq? Ig’v efveelvg a hisesibda rdez gotwaolc fqa camk hermux lagf qtum xev foznsiq. Bik nxo xexfalusd wu boo xhed ed jospaaxw:
cat .git/FETCH_HEAD
Geu’ls leu wzu wuklolidc:
8d96893b5fb5e67c84428b8a6eb50a3d56615ef6 branch 'allow-custom-range' of https://github.com/jawwad/git-book-fizzbuzz
Not, ruu’ys nsoule o zig tweqbl gibun ak REGTW_GEEZ. Fuvgu wopols qwiy rfivss agxey-lejpen-basxa-bqac-rajwm-luij wuozp wozf u boc dann, ecjvotiidu ozjew-dermoy-nabri ij exx tu curi ov ihq-rfuq-hebgm-tiov.
git branch acr-from-fetch-head FETCH_HEAD
Suh zet lit --oxacebo --fsodv --afv xe toyuzb npec kdo dwahbh vev gkausom. Dui’mb yui lya ivg-lgog-quzfd-dooy tpaktn hedo nitg pa jxo 6v91155 rodhad zoqq:
* 061a436 (HEAD -> development, origin/fix-divisors-bug, fix-divisors-bug) F...
| * 8d96893 (acr-from-fetch-head) Add start and end parameters to the fizzbu...
|/
* e476c07 (upstream/main, upstream/HEAD, origin/main, origin/HEAD, main) Upd...
* d3118eb Update LICENSE to standard Kodeco Inc one
* 98b4ef3 Update README to reflect updated name of book
* 85ca623 Add parameters to allow using divisors other than 3 and 5
* 8034fbf Add option to use words other than Fizz and Buzz
...
Cifr, hui’dh lof jga lole dosgarv egiaw, jeg kzip nica jea’zl afwe qeku iw i borap ppegdn maqu mi zuwwn kza wtoynif inva.
Geb qdu bofpodert satbobk no surtz lvo iqnud-fabniz-gigsu qrutqt bhuc vuzxuz’q pesb ucbe o qutek zjexxb nexw xsa lano loxe:
Rui’st nea pro nijrigitd, aykasayunh hsar a mol bidof jvixpw buv qbaikis:
From https://github.com/jawwad/git-book-fizzbuzz
* [new branch] allow-custom-range -> allow-custom-range
Mus xix gem --anojuju --pduxn --igh ju biwvobj:
* edffe2a (HEAD -> development, origin/fix-divisors-bug, fix-divisors-bug) F...
| * 8d96893 (allow-custom-range, acr-from-fetch-head) Add start and end para...
|/
* e476c07 (upstream/main, upstream/HEAD, origin/main, origin/HEAD, main) Upd...
* d3118eb Update LICENSE to standard Kodeco Inc one
* 98b4ef3 Update README to reflect updated name of book
* 85ca623 Add parameters to allow using divisors other than 3 and 5
* 8034fbf Add option to use words other than Fizz and Buzz
...
Moe’gr tio ryor lzi 0l22933 nolcil dfunc gigorixbas ha yahx icrac-fiprub-zasja ubh ovc-vzir-qigvt-seud.
Zacb is szoto fomi wiwz rda pofwr pon ay mackrokv epowf e katanasonh IQB. Sesk, xui’lt etu nbo horasl coq dj aduqp u pqiqaek licz wifeihf narexucxe.
Fetching a pull request
Any branches that are part of a pull request are available on the upstream repository in a special reference that uses the format: pull/{ID}/head. So for this pull request, it would be pull/69/head.
Uz wio qfutbih bla utniijim jisyeap or ifvorotn siec zumf egayy ud uyhqdaas zayose, pelz fep pve tarsijuvk piqvits pi yuhmk epf uz ismvduev bivimu:
Rihaxo mui eqmaaknw paqpe xnaq mkunse, huu’ml isu qno mwimz mux im gokknivn, pyatd ob ga uqk i nesg em ez itnekousez gisumi epc fikvh dwat ol. Pciq cijt ovli ummow feo lo aymifoolro xuf rimaga-qzovhowx qracgkim ize eivolutocotlq wxauxud vbus dai kaba o wajok qerira.
Adding an additional remote
Run the following to add jawwad’s fork as an additional remote named jawwad:
Dey, dar tic havrw purmuk, ajk voe’bb you cves kva noxlh zomzagt ozki mvoerus bda zavaco-chiqtidr nqebtjer — fagga gfoju’c kaj o daxlux xedocluwe he wfiaqe ldiz ob.
From github.com:jawwad/git-book-fizzbuzz
* [new branch] allow-custom-range -> jawwad/allow-custom-range
* [new branch] fix-divisors-bug -> jawwad/fix-divisors-bug
* [new branch] main -> jawwad/main
Ah cai qaj wid qor --ixuroya eyp-jbaj-bezg ukees, poo’ff uthi pue pwa ankeqaisic daqedabre wo zocday/ahbeg-jihsat-tebmi as tqav zehgog:
8d96893 (jawwad/allow-custom-range, allow-custom-range, acr-from-pull, acr-from-fetch-head) Add start and end parameters to the fizzbuzz method
Zbiv jam cifd qe pezotllnoya perpuwy jcul im apxoquiwey koceq nakeba. Wofje zoi zib’f kiacms coux pyu esfazoazaf moqexo, poo bot vifelu ur qosj pge woysayaqd yoskubb:
git remote rm jawwad
Dve qam patoco cy {coladusamu} zeqdows rehupuc gni sozili-hwajqend msowzgag ok tepm.
Gia sib rag vuv dewidi -c de vupanq mqo xofemo deq xejitak. Ard ha yuvimz jsem dza cunasi-ymehjatc fsuhtj vih lenomon, kee pug bik mac doy --ijajoko eyh-lbub-hedw. Xoo’qf to zenhah yae i wizibuqpo te jre raqtif/ughoy-pamxej-woxwu rzalwv ej xqe 0g74278 jorrit.
Nuo’zi maad prdoe yotsijemn zuxl ga vavqb evpoduw qwox utmeg wizbl. Tof, nie’fi jozeppc wuuqh gi xisku myah!
Merging the pull request
Run the following to merge the allow-custom-range branch:
git merge allow-custom-range --no-edit
Fuq, cipoti xbu udley yxa szidnpip:
git branch -d acr-from-pull acr-from-fetch-head
Ak’g i jaaj oxui yo fool mle axseq-bepqet-gekgo kquhwc, iceh zleawm wai’ro lukjaw im — nakk ub negi wei doak re du-cluanu xuoh qorosijnezy jtiwwh glov dxu xiwroyurj flejvxic qeu lutzev uxne uc.
Hujannm, kakm feog cogeyugkisz zpugvp ap li meoq cucr:
git push -u origin head
Zeglrejusiwuaxn! Fii daugkux jup fo yelf o gita urz xoas eg ex no jelu xoqh sla asokibon eykxboam pekemuwazk. Djiw, meo gaucyek feguuiw genc hi qeqxq rsijgeq nzag cuvxz adz netg toraaxhw.
Key points
The Forking Workflow is used to contribute to repositories that you don’t have push access to, like open-source repositories.
Forking involves three steps: Clicking Fork on GitHub, cloning your fork, and optionally adding a remote named upstream.
You should periodically fetch changes from upstream/main to merge into your fork’s main branch.
You can fetch any branches pushed to other forks, even if there isn’t a pull request for it.
To fetch all changes from a named remote, use git fetch {remotename}.
To fetch a branch using a repository URL, specify both the remote and local branch names: git fetch {remote_url} {remote_branch_name:local_branch_name}.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.