Как да не се страхуваме повече от Git

Разбиране на машината за премахване на несигурността

Били ли сте тук преди? (уеб комикс от XKCD)

Какво е Git все пак?

„Това е система за контрол на версиите.“

Защо ми трябва?

"За контрол на версиите, глупак."

Добре, добре, все още не съм много полезна. Ето основната идея: Тъй като проектите стават твърде големи, с твърде много сътрудници, става невъзможно да се проследи кой какво е направил и кога. Някой въведе ли промяна, която разби цялата система? Как разберете каква е била тази промяна? Как се връщате към нещата преди? Обратно към непокътнатата страна на чудесата?

Ще го направя още крачка - кажете не проект с много сътрудници, а малък проект с вас като създател, поддържащ и дистрибутор: Вие създавате нова функция за този проект, която въвежда фини грешки, за които ще разберете по-късно. Не помните какви промени сте направили в съществуващата кодова база, за да създадете тази нова функция. Проблем?

Отговорът на всички тези проблеми е версия! Наличието на версии за всичко, което сте кодирали, гарантира, че знаете кой е направил промените, какви промени и къде точно, от началото на проекта!

И сега ви каня да спрете да мислите за (ж) това като черна кутия, отворете я и разберете какви съкровища ви очакват. Разберете как работи Git и никога повече няма да имате проблем с работата на нещата. След като преминете през това, обещавам, че ще осъзнаете глупостта да правите това, което казва комиксът XKCD по-горе. Точно това се опитва да предотврати версията.

Как да Git?

Предполагам, че знаете основните команди в Git или сте чували за тях и сте ги използвали поне веднъж. Ако не, ето основен речник, който ще ви помогне да започнете.

Хранилище: място за съхраняване на неща. При Git това означава вашата папка с код

head: „указател“ към най-новия код, върху който сте работили

добавяне: Действие, за да помолите Git да проследи файл

ангажиране: Действие за запазване на текущото състояние - такова, че човек може да прегледа това състояние, ако е необходимо

дистанционно: хранилище, което не е локално. Може да бъде в друга папка или в облака (например: Github): помага на други хора да си сътрудничат лесно, тъй като не е нужно да получават копие от вашата система - просто могат да го получат от облака. Също така, гарантира, че имате резервно копие в случай, че счупите вашия лаптоп

дърпа: Действие за получаване на актуализиран код от дистанционното

push: Действие за изпращане на актуализиран код на дистанционното

сливане: Действие за комбиниране на две различни версии на код

status: Показва информация за текущото състояние на хранилището

Къде да Git?

Представяме магията, контролирана от скрита папка: .git /

Във всяко git хранилище ще видите нещо подобно

$ tree .git /
.git /
├── Глава
├── конфиг
├── описание
├── куки
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── предварително ангажиране.пример
│ ├── предварителна проба.пример
│ ├── pre-rebase.sample
│ ├── предварително получаване.пример
│ ├── подготвяне-ангажиране-msg.sample
│ └── update.sample
├── информация
│ └── изключете
├── обекти
│ ├── информация
│ └── пакет
└── реф
    ├── глави
    └── тагове
8 директории, 14 файла

Ето как Git контролира и управлява целия ви проект. Ще влезем във всички важни битове, един по един.

Git се състои от 3 части: магазин за обекти, индекс и работна директория.

Магазинът на обектите

Ето как Git съхранява всичко вътрешно. За всеки файл във вашия проект, който добавяте, Git генерира хеш за файла и съхранява файла под този хеш. Например, ако сега създам helloworld файл и направя git добави helloworld (което казва на Git да добави файла, наречен helloworld, в магазина на git object), получавам нещо подобно:

$ tree .git /
.git /
├── Глава
├── конфиг
├── описание
├── куки
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── предварително ангажиране.пример
│ ├── предварителна проба.пример
│ ├── pre-rebase.sample
│ ├── предварително получаване.пример
│ ├── подготвяне-ангажиране-msg.sample
│ └── update.sample
├── индекс
├── информация
│ └── изключете
├── обекти
│ ├── a0
│ │ └── 423896973644771497bdc03eb99d5281615b51
│ ├── информация
│ └── пакет
└── реф
    ├── глави
    └── тагове
9 директории, 16 файла

Създаден е нов обект! За тези, които се интересуват от преминаването под капака, Git използва вътрешно командата hash-object така:

$ git хеш-обект helloworld
a0423896973644771497bdc03eb99d5281615b51

Да, това е същият хеш, който виждаме в папката с обекти. Защо поддиректорията с първите два знака на хеша? Това прави търсенето по-бързо.

След това Git създава обект с името като горния хеш, компресира дадения ни файл и го съхранява там. Следователно, всъщност можете да видите и съдържанието на обекта!

$ git cat-file a0423896973644771497bdc03eb99d5281615b51 -p
Здравей свят!

Всичко това е под капака. Никога не бихте използвали котешки файл в ежедневните добавки. Просто ще добавите и ще оставите Git да се справи с останалите.

Това е първата ни команда на Git, изпълнена и прах.

git add създава хеш, компресира файла и добавя компресирания обект в обекта.

Работната директория

Както подсказва името, тук работите. Всички файлове, които създавате и редактирате, са в работната директория. Създадох нов файл, byeworld и изпълних статус на git:

$ git статус
На майстор на клона
Все още няма ангажименти
Промени, които трябва да бъдат извършени:
  (използвайте "git rm --cached  ..." до неустойчивост)
нов файл: helloworld
Непроследени файлове:
  (използвайте "git add  ...", за да включите в това, което ще бъде извършено)
byeworld

Непроследените файлове са файлове в работната директория, които не сме поискали git да управляваме.

Ако в работната директория нищо не бяхме направили, ще получим следното съобщение:

$ git статус
На майстор на клона
нищо за ангажиране, работещо дърво чисто

което съм почти сигурен, че сега разбирате. Игнорирайте клона и се ангажирайте засега. Ключът е, че работното дърво (директория) е чисто.

Индексът

Това е ядрото на Git. Известен също като зоната за постановка. Индексът съхранява картографирането на файловете в обектите в обектния магазин. Тук влизат ангажиментите. Най-добрият начин да видите това е да го изпробвате!

Нека се ангажираме с добавянето на файла helloworld

$ git commit -m "Добави helloworld"
[master (root-commit) a39b9fd] Добавете helloworld
 1 файл променен, 1 вмъкване (+)
 създаване на режим 100644 helloworld

Обратно към нашето дърво:

$ tree .git /
.git /
├── COMMIT_EDITMSG
├── Глава
├── конфиг
├── описание
├── куки
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── предварително ангажиране.пример
│ ├── предварителна проба.пример
│ ├── pre-rebase.sample
│ ├── предварително получаване.пример
│ ├── подготвяне-ангажиране-msg.sample
│ └── update.sample
├── индекс
├── информация
│ └── изключете
├── трупи
│ ├── Глава
│ └── реф
│ └── глави
│ └── господар
├── обекти
│ ├── a0
│ │ └── 423896973644771497bdc03eb99d5281615b51
│ ├── a3
│ │ └── 9b9fdd624c35eee08a36077f411e009da68c2f
│ ├── fb
│ │ └── 26ca0289762a454db2ef783c322fedfc566d38
│ ├── информация
│ └── пакет
└── реф
    ├── глави
    │ └── господар
    └── тагове
14 директории, 22 файла

Ах, интересно! В нашия магазин за обекти имаме 2 нови обекта и някои неща, които все още не разбираме в регистрационните файлове и справки. Връщане към нашия котешки файл на нашия приятел:

$ git cat-file a39b9fdd624c35eee08a36077f411e009da68c2f -p
дърво fb26ca0289762a454db2ef783c322fedfc566d38
автор = <=> 1537700068 +0100
поверителя = <=> 1537700068 +0100
Добавете helloworld
$ git cat-file fb26ca0289762a454db2ef783c322fedfc566d38 -p
100644 blob a0423896973644771497bdc03eb99d5281615b51 helloworld

Както можете да се досетите, първият обект са метаданните за ангажиране: кой какво е направил и защо с дърво. Вторият обект е действителното дърво. Ако разбирате файловата система на Unix, ще знаете какво точно е това.

Дървото в Git съответства на файловата система Git. Всичко е или дърво (директория), или blob (файл) и с всеки ангажимент, Git също съхранява информацията за дървото, за да си каже: това трябва да изглежда работната директория в този момент. Обърнете внимание, че дървото сочи към конкретен обект от всеки файл, който съдържа (хешът).

Време е да поговорим за клонове! Първият ни ангажимент добави още няколко неща към .git /. Интересът ни сега е в .git / refs / heads / master:

$ cat .git / refs / heads / master
a39b9fdd624c35eee08a36077f411e009da68c2f

Ето какво трябва да знаете за клоновете:

Клонът в Git е лек подвижен показалец към една от тези команди. Името на клона по подразбиране в Git е master.

Е, какво? Обичам да мисля за клонове като вилица във вашия код. Искате да направите някои промени, но не искате да нарушавате нещата. Вие решавате да имате по-силно разграничаване от дневника за извършване на ангажименти и там влизат клоните. Master е клонът по подразбиране, използван също и като производствен клон фактически. Следователно създаването на горния файл. Както можете да се досетите по съдържанието на файла, това сочи първия ни ангажимент. Следователно, това е указател на ангажимент.

Нека да разгледаме това допълнително. Кажете, създавам нов клон:

$ git разклонение окончанието
$ git клон
* майстор
  краят

Имаме го, нов клон! Както можете да се досетите, нов запис трябва да е добавен към .git / refs / heads / и тъй като няма допълнителен ангажимент, той трябва да сочи и първия ни ангажимент, също като master.

$ cat .git / refs / heads / окончанието
a39b9fdd624c35eee08a36077f411e009da68c2f

Да, точно! Сега, помниш ли Byeworld? Този файл все още не е бил следен, така че без значение в кой клон се премествате, този файл винаги ще бъде там. Кажете, сега искам да премина към този клон, така че ще проверя клона, като пушек.

$ git checkout край-край
Превключва към разклонение „крайния край“
$ git клон
  майстор
* краят

Сега, под капака, Git ще промени цялото съдържание на работната директория, за да съответства на съдържанието, посочено от филиалния ангажимент. Засега, тъй като това е абсолютно същото като майстор, изглежда същото.

Добавям и ангажирам файла byeworld.

Какво очаквате да промените в папката с обекти?

Какво очаквате да промените в папката refs / heads?

Помислете за това, преди да продължите напред.

$ tree .git /
.git /
├── COMMIT_EDITMSG
├── Глава
├── конфиг
├── описание
├── куки
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── предварително ангажиране.пример
│ ├── предварителна проба.пример
│ ├── pre-rebase.sample
│ ├── предварително получаване.пример
│ ├── подготвяне-ангажиране-msg.sample
│ └── update.sample
├── индекс
├── информация
│ └── изключете
├── трупи
│ ├── Глава
│ └── реф
│ └── глави
│ ├── господар
│ └── окончанието
├── обекти
│ ├── 0b
│ │ └── 17be9dbc34c5a5fbb0b94d57680968efd035ca
│ ├── a0
│ │ └── 423896973644771497bdc03eb99d5281615b51
│ ├── a3
│ │ └── 9b9fdd624c35eee08a36077f411e009da68c2f
│ ├── b3
│ │ └── 00387d818adbbd6e7cc14945fdf4c895de6376
│ ├── d1
│ │ └── 8affe001488123b496ceb34d8b13b120ab4cb6
│ ├── fb
│ │ └── 26ca0289762a454db2ef783c322fedfc566d38
│ ├── информация
│ └── пакет
└── реф
    ├── глави
    │ ├── господар
    │ └── окончанието
    └── тагове
17 директории, 27 файла

3 нови обекта - 1 за добавяне, 2 за ангажиране! Има смисъл? Какво мислите, че съдържат предметите?

  • Ангажиране на метаданни
  • добавете съдържание на обект
  • Описание на дървото

Последната част от картината е: как работят тези метаданни за ангажиране с предишните метаданни за ангажиране. Е, коте-файл!

$ git cat-file 0b17be9dbc34c5a5fbb0b94d57680968efd035ca -p
100644 blob d18affe001488123b496ceb34d8b13b120ab4cb6 byeworld
100644 blob a0423896973644771497bdc03eb99d5281615b51 helloworld
$ git cat-file b300387d818adbbd6e7cc14945fdf4c895de6376 -p
дърво 0b17be9dbc34c5a5fbb0b94d57680968efd035ca
родител a39b9fdd624c35eee08a36077f411e009da68c2f
автор = <=> 1537770989 +0100
комитет = <=> 1537770989 +0100
добавете byeworld
$ git cat-file d18affe001488123b496ceb34d8b13b120ab4cb6 -p
Чао свят!
$ cat .git / refs / heads / окончанието
b300387d818adbbd6e7cc14945fdf4c895de6376

Виждате ли го с удебелен шрифт? Родителният показалец! И точно така сте мислили за това - свързан списък, свързващ ангажиментите заедно!

А виждате ли внедряването на клона? Той сочи ангажимент, последният, който направихме след проверката! Разбира се, господарят все още трябва да насочва към helloworld ангажимента, нали?

$ cat .git / refs / heads / master
a39b9fdd624c35eee08a36077f411e009da68c2f

Добре, преживяхме много, нека го обобщим до тук.

TL; DR

Git работи с обекти - компресирани версии на файлове, за които искате Git да проследява.

Всеки обект има идентификатор (хеш, генериран от Git въз основа на съдържанието на файла).

Всеки път, когато добавите файл, Git добавя нов обект в магазина на обекти. Точно поради тази причина не можете да се справяте с много големи файлове в Git - той съхранява целия файл всеки път, когато добавяте промени, а не разликата (противно на общоприетото схващане).

Всеки ангажимент създава 2 обекта:

  1. Дървото: Идентификационен номер за дървото, който действа точно като unix директория: сочи към други дървета (директории) или blobs (файлове): Това изгражда цялата структура на директория въз основа на присъстващите по това време обекти. Blobs са представени от текущите обекти, създадени чрез add.
  2. Метаданните за ангажиране: Идентификатор за ангажимента, който е извършил ангажимента, дърво, което представлява съобщението за ангажиране, ангажиране и родителски ангажимент. Образува свързана структура на списъка, свързваща ангажименти заедно.

Клоните са указатели за извършване на метаданни обекти, всички съхранявани в .git / refs / heads

Това е всичко за разбирането зад кулисите! В следващата част ще преминем през някои от действията на Git, които дават на хората кошмари:

нулиране, сливане, дърпане, натискане, извличане и как те променят вътрешната структура в .git /.

Други истории от тази поредица:

  • Как да не се страхуваме повече от Вим
  • Как да не се страхуваме повече от Python

Наслаждавахте се на това? Не пропускайте публикация отново - абонирайте се за моя пощенски списък!