Комитиране, натискане, изтегляне... Всеки разработчик знае основния речник на контрола на версиите с Git в наши дни. Но освен тези основи, в Git има цял свят от други, по-разширени функции. И точно тези функции могат драматично да подобрят начина, по който създавате софтуер! Тези инструменти могат да ви помогнат...

  • Отмяна на грешки
  • Откривайте грешки бързо
  • Почистете хронологията на ангажиментите си
  • Интегрирайте кода на трета страна по добре структуриран начин
  • …и още много!

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

Но сега: да вървим!

Използване на Reflog за отмяна на грешки

„Reflog“, въпреки че е изключително полезен, е доста малко известна функция в Git. Накратко, можете да мислите за Reflog като за „дневника“ на Git; Git го използва, за да протоколира всички движения на показалеца HEAD, които се случват във вашето локално хранилище. Това означава, че всеки път, когато ангажирате, пребазирате, сливате, нулирате или избирате череша, Git ще вземе под внимание това в този специален дневник. Такъв протокол от вашите най-важни действия е, разбира се, перфектна предпазна мрежа, когато сте направили грешка!

Нека да разгледаме един прост пример: не сте доволни от последните си няколко ангажимента и искате да се отървете от тях. Извършвате „нулиране“, като връщате назад хронологията на ангажиментите си с няколко ангажимента — и по този начин премахвате тези нежелани ангажименти. Да кажем също, че няколко минути по-късно осъзнавате, че не е трябвало да правите това и сега отчаяно искате тези ангажименти обратно! 😱

Това е мястото, където Reflog идва на помощ! Разгледайте следното малко видео, което показва както нашата грешка (извършване на „нулиране“), така и използването на Reflog за отмяна на тази грешка:

Забележка: Използвам клиента Tower Git в горния пример за по-лесна визуализация на работния процес.

Забележете как използвах „git reset“ още веднъж, за да върна старото състояние. Като алтернатива можех да използвам и „git branch“, ако предпочитах старото състояние да е в отделен клон.

Ако искате да научите повече за отмяната на грешки с Git, тогава разгледайте (безплатната) видео поредица „Комплект за първа помощ за Git“.

Използване на Bisect за намиране на грешки

Сигурен съм, че следната ситуация ще ви прозвучи познато. Вие знаете, че вашият код е работил в някое време, но не знаете точно кога (в кой комит) сте въвели някакъв неприятен бъг ! Как можете да намерите ангажимента, който съдържа грешката? Отговорът на Git на този въпрос е неговият инструмент „Bisect“.

Bisect изисква да му кажете един комит хеш, където нещата са определено наред. След като му подадете хеша на този безопасен комит, Git знае, че има „лош“ ангажимент някъде между сега и този „добър“ комит. Това, което Git прави сега, е доста просто, но и доста ефективно; той разделя диапазона от ангажименти наполовина и проверява комита в средата.

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

Git Bisect на практика

И така, как „git bisect“ работи на практика? Първата стъпка е изрично да стартирате процеса:

$ git bisect start

Все още не се е случило много, защото Git чака да предоставите както „добър“, така и „лош“ ангажимент. Да започнем с лошия ангажимент. В повечето случаи това е лесно, защото (вероятно) текущото състояние е погрешно — така че можем просто да предоставим „HEAD“ като лоша версия:

$ git bisect bad HEAD

Втората информация, ревизия, която определено е „добра“, може да се нуждае от малко ровене. Можете да започнете, като проверите по-стара версия, където сте напълно уверени, че всичко все още е наред. Да кажем, че намерихме такава „добра“ ревизия:

$ git bisect good fcd61994

След това Git ще започне действителния процес на „разполовяване“ и ще провери ревизия в средата на този диапазон от ангажименти между „добрия“ и „лошия“:

Bisecting: 3 revisions left to test after this (roughly 1 step)
 [0023cdddf42d916bd7e3d0a279c1f36bfc8a051b] Changing page structure

Топката вече е в нашето поле. След като Git е проверил този комит по средата, трябва да изпълним или изградим нашето приложение и да тестваме, за да видим дали грешката все още е налице или не. И ние трябва да кажем на Git резултата от нашия малък тест или с „git bisect bad“ или „git bisect good“.

В зависимост от нашия отговор, Git след това ще повтори процеса и ще раздели или първата, или втората половина на първоначалния обхват на ангажименти — и отново ще провери комит в средата и ще ви покани да тествате. Като повторите този процес няколко пъти, вие ще откроите лошия ангажимент доста бързо!

След като откриете виновника, можете да прекратите процеса на разполовяване:

$ git bisect reset

Това ще инструктира Git да завърши разполовяването и ще ви върне към предишния HEAD комит.

Управление на големи файлове с Git LFS

В наши дни софтуерните проекти често включват нещо повече от файлове с обикновен текстов код: визуални активи като изображения или филми, аудио файлове и много други видове активи са много често срещани днес. И наистина, защо трябва да предавате прекрасните предимства на контрола на версиите за тези типове файлове? Да знаете, че вашите файлове са безопасни, че не могат да бъдат презаписани по погрешка и могат да бъдат върнати към предишни версии са предимства, които са също толкова ценни за големите двоични файлове.

Въпреки това, в миналото много екипи все пак бяха решили да НЕ включват големи двоични файлове в своите Git хранилища – и по разбираема причина; чрез добавяне на големи файлове техните Git хранилища се разраснаха много бързо и в крайна сметка станаха почти неуправляеми. Когато нов съотборник трябваше да клонира едно от тези Git хранилища, той оставаше с гигабайти изтегляния и раздуто локално Git хранилище.

LFS слага край на „раздуването на големи файлове“

Нещата станаха много по-добри, когато преди няколко години беше пуснато разширението „Git LFS“. „LFS“ означава „Large File Storage“ и е свързано с подобряване на работата с големи двоични файлове. LFS прави това чрез добавяне на два критични нови компонента: локален LFS кеш и отдалечено LFS хранилище.

  • Локален LFS кеш: След като кажете на Git, че определен файл (или тип) трябва да се управлява чрез LFS, тези файлове се записват само като указатели в локалното хранилище. След това действителните файлови данни се намират някъде другаде: в локалния LFS кеш, който сега придружава вашето локално Git хранилище.
  • Отдалечено LFS хранилище: На дистанционното, ново LFS хранилище запазва и доставя всички тези големи файлове при поискване.

Всеки път, когато Git срещне файл, управляван от LFS във вашето локално хранилище, той ще намери само указател, а не действителните данни на файла. След това Git ще поиска от локалния LFS кеш да го достави. За да направи това, локалният LFS Cache търси файла по своя указател - и ако все още не го има, той го изисква от отдалечения LFS Store.

Това е голямата революция: трябва само да имате файловите данни на диска, които са ви необходими в момента — за точната версия, която сте проверили. Всичко друго може да бъде изтеглено при поискване. Например, когато проверите различна версия, която има различни версии на тези големи двоични файлове.

Git LFS на практика

И така, как работи Git LFS на практика?

1. Инсталация: Преди да можете да използвате LFS, трябва да инсталирате съответното разширение. Има подробно описание, включително връзки за изтегляне в GitHub.
2. Проследяване на файлове: След това трябва да кажете на Git кои файлове или типове файлове искате да проследява чрез LFS:

# Tracking a single, specific file:
$ git lfs track “design.psd”
 
# Tracking certain file types:
$ git lfs track “*.mov”

От този момент нататък новите файлове, които отговарят на шаблоните за проследяване, които сте задали, ще се обработват чрез LFS. Ползите се забелязват веднага. Когато клонирате такова хранилище, НЯМА да останете с гигабайти данни, от които всъщност не се нуждаете. Вместо това ще получите само данните, които са необходими за текущата версия,всичко друго ще бъде доставено при поискване, когато имате нужда от него.

Ако искате да научите малко повече за LFS и как да работите с него, препоръчвам ви да погледнете статията „„Работа с големи файлове с LFS““.

Пребазиране

Git предлага два основни начина за интегриране на един клон в друг: Обединяване и Пребазиране. Въпреки че Rebase вече не е точно „тайна“ или „професионална функция“, само няколко души разбират напълно как работи.

Преди да навлезем в подробностите тук, нека изясним един важен момент: Rebase не е „по-добро“ от Merge. Има много екипи, които умишлено избират Merge пред Rebase по добри причини. Има и други отбори, които предпочитат Rebase в определени ситуации. Всичко се свежда до лични предпочитания и ясни насоки за вашия екип.

Историята на ангажиментите като права линия

Нека първо поговорим за защо може да искате да използвате Rebase вместо Merge. Ето класически пример за резултата от използването на „git merge“:

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

Rebase ще създаде права линия на историята, за разлика от Merge. Няма видим „възел“, нито ангажимент за сливане, който да показва, че два клона действително са интегрирани. Както беше казано по-рано, някои екипи - в някои ситуации - предпочитат този начин на интеграция.

Но как всъщност работи Rebase?

Механиката зад повторното базиране

Първата стъпка, разбира се, е всъщност да стартирате процеса на Rebase, като кажете на Git кой клон искате да интегрирате в текущия си клон HEAD:

$ git rebase branch-B

Сега, в нашия текущ клон HEAD, Git ще „отмени“ всички ангажименти, които са се случили след разклоняването му. Няма нужда да се притеснявате обаче, тъй като Git няма да отхвърли тези ангажименти! Вместо това можете да мислите за тези ангажименти като за „паркирани временно“:

След това Git ще приложи ангажиментите, които искаме да интегрираме от клон-B. В този момент, по средата на операцията Rebase, двата клона изглеждат абсолютно еднакви:

В последната стъпка „паркираните“ ангажименти на клон-A вече се прилагат отново, но вече върху интегрираните ангажименти от клон-B. Те се основават на нов предшественик - оттук и терминът "пребаза".

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

Има още много какво да се каже за повторното базиране, разбира се! Ако сте любопитни, вижте статията „Rebase като алтернатива на Merge““.

Подмодули

Много съвременни софтуерни проекти разчитат на код от други проекти, библиотеки на трети страни и рамки или просто вашата собствена колекция от инструменти и помощници. Във всеки случай няма да намерите много проекти, които не трябва да включват код от някъде другаде.

Въпреки че можете просто да интегрирате външен код чрез „копиране и поставяне“, скоро ще откриете, че това е много податлив на грешки подход. Какво се случва, когато библиотеката или рамката се актуализират? Повтаряте ли процеса на копиране и поставяне отново и отново?! И което е много по-важно, кодът от друг „проект“ просто не трябва да бъде част от Git хранилището на вашия собствен проект! Това е различен проект и следователно трябва да се намира в собственото си Git хранилище.

Git предлага много елегантен и практичен начин за тези случаи на употреба: Подмодули. Накратко, подмодулите са Git хранилища вътре в други Git хранилища. Използването на подмодули за интегриране на друг код предлага няколко важни предимства, включително:

  • Чисто разделяне: Най-важното е, че имате чисто разделение между кода на вашия проект и допълнителния код от други източници.
  • Актуализиране на кода на трета страна: Освен това става много лесно да актуализирате кода на трета страна. Просто отваряте подхранилището и изтегляте нови промени, както бихте направили във всяко друго Git хранилище!
  • Стандартни Git хранилища: Освен всичко това, подмодулите са само стандартни Git хранилища! Това означава, че можете да правите промени, да се ангажирате или да дърпате, както сте свикнали.

Добавяне на подмодул към проект

За да добавите подмодул към вашия проект, просто отидете в папката, където искате да го изтеглите, и използвайте командата „git submodule add“:

# Move into the folder where you want the Submodule files:
$ cd lib
 
# Add the Submodule repository from its remote URL:
$ git submodule add https://github.com/djyde/ToProgress
 
# Adding a Submodule, like any other change, must be committed to the repo:
$ git commit -m “Add Submodule to project”

Това ще изтегли проекта на посоченото място и ще създаде необходимата конфигурация във вашето локално Git хранилище. Git управлява конфигурацията на подмодула на множество места; главно в “.gitmodules”, но също и във вашия локален файл “.git/config” и във вътрешната папка “.git/modules”. Конфигурирането на подмодулите е доста сложна и колеблива тема, поради което бих искал да ви дам един съвет: никога не се забърквайте с подмодулите ръчно! Винаги използвайте подходящите команди или използвайте графичен интерфейс на работния плот, като Tower за този вид работа!

Клониране на отдалечен проект с подмодули

Да приемем, че искате да клонирате съществуващ проект, който съдържа подмодули. Ако трябваше да използвате обикновена команда „git clone“, може да се изненадате да видите, че след като клонирането приключи, няма нито един от вашите подмодулни файлове. Можете да коригирате това по един от следните начини:

(a) Можете просто да добавите опцията --recurse-submodules към командата „git clone“. Това ще инструктира Git също да инициализира всички подмодули, след като клонирането приключи.

(b) Като алтернатива, ако сте използвали обикновена команда „git clone“ без тази опция, ще трябва да ръчно инициализирате подмодулите след това с помощта на git submodule update --init --recursive.

Така или иначе вашият клониран проект ще също съдържа файловете на всички свързани подмодули.

Проверка на ревизия в подмодул

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

Представете си какво би се случило, ако подмодулите, като нормалните хранилища на Git, бъдат проверявани в клонове. Естеството на разклоненията е, че действителното им съдържание се променя с времето; когато пристигнат нови ангажименти, те автоматично преместват указателите си към най-новия комит. В подмодула обаче често не искате това. По-скоро искате да сте 100% сигурни за точния код, който подмодулът въвежда във вашия проект. Ето защо подмодулите винаги сочат към конкретна версия, която гарантира, че винаги имате правилния код в основния си проект.

Да се ​​научим да работим с подмодули

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

След като разберете как работят, ще бъдете много по-уверени в използването на подмодулите на практика!

Заключение

На пръв поглед, след овладяване на основите и известно ангажиране, дърпане и натискане, изглежда, че Git не е „голямата работа“, за която някои хора го представят. Но при втори поглед ще откриете много мощни функции; някои от които имат потенциала наистина да подобрят начина, по който пишете софтуер. Надявам се, че сте намерили тази статия за полезна. Забавлявайте се, откривайки силата на Git!

за автора

Тобиас Гюнтер е главен изпълнителен директор на Tower, популярния десктоп клиент Git, който помага на повече от 100 000 разработчици по целия свят да бъдат по-продуктивни с Git.