Днес
- Ще се занимаваме с Git
- Ще погледнем под капака
- Ще го разберем
Защо отделяме два часа, за да говорим за Git?
- Защото очевидно е нещо, което е необходимо и се ползва ежедневно
- Дори да си мислим, че го знаем, все ще има нещо ново, което ще научим
- Git e интересен пример за елегантно и семпло техническо решение на сравнително сложен проблем
Version Control System (VCS)
Не просто система, следяща промени в код!
- Съхранение
- Сигурност - всичко е обратимо
- Лесна работа в екип
- Различни версии на кода
- Нещо, без което не можем
Познавате ли този човек?
Linus Torvalds
Бащата на Linux
Създател на Git
Малко история
Linux kernel
- 15,803,499 реда код към юли 2013
- 19,509,218 реда за Linux 4.1
- 13,708 автори
- ~24 години история
Какво е нужно?
- Бързина
- Много разклонения на кода
- Много възможности
- Децентрализация
Централизирана VCS
- Един централен сървър
- Всички теглят и публикуват кода на него
- Локално се пазят само най-необходимите неща
Децентрализирана VCS
- Всеки има копие на цялата история
- Често отново има един основен сървър...
- ...а може и изобщо да няма сървъри
- Почти всички операции се правят локално - не е необходима връзка с Интернет
- Когато решите - синхронизирате се с който и да е друг сървър
Защо точно git?
- Гъвкавост
- Бързина
- Страшен на пръв поглед, може би и на втори
- Когато разберете как работи, ще ви озари ярка светлина и животът ви вече ще има смисъл
- Ще ви отнеме много време да откриете всичко, на което е способен
- Но не е нужно да знаете всичко, за да ви е полезен
В началото беше git init
Създава празно хранилище в текущата директория.
Виждате папката .git
- съхранява всичко, което е нужно за историята.
Клониране на хранилище
git clone https://github.com/fmi/ruby-lectures.git
- Компресия - сваля се бързо
- Поддържа и други протоколи като
file
, ssh
и git
Работната директория (working directory)
- Директорията на проекта - всичко без
.git
- Текущата версия на файловете
Хранилището
- Там се намират файловете, които се следят от Git
- Съхраняват се в
.git
Staging област
- Нарича се и индекс
- Междинна област между работната директория и хранилището
- Съхранява файловете, които ще отидат в следващия commit
- Просто един файл -
.git/index
Отдалечено хранилище
- Друго хранилище
- Най-често се намира на друг компютър
- Може да е просто друга
git
директория
- Локалното и отдалеченото хранилище се синхронизират чрез
git pull
и git push
- Може да има повече от едно такова
- А може и да няма нито едно
Четирите области
Четирите области
- Един файл може да е в повече от една област едновременно
- Възможно е този файл да е с различно съдържание във всяка една област
- Хранилищата съдържат много версии на един и същ файл
4 състояния на файл
Untracked
- няма версии на този файл в staging областта и локалното хранилище (нов файл)
Unmodified
- файлът е с еднакво съдържание в работната директория, staging и хранилището
Modified
- файлът е с различно съдържание в работната директория и staging
Staged
- файлът е с еднакво съдържание в работната директория и staging, но се различава в хранилището
- Ако не сме променили състоянието на файл в staging, то то е същото като в текущия commit в хранилището
4 състояния на файл
Staging областта
добавяне на файлове
git add new_or_modified.rb
git add lectures/11
git add .
- Добавя един или повече файлове към staging областта.
Staging областта
информация
- Списък с всички променени, изтрити и нови файлове.
Staging областта
промени
- Промени на файловете в работната директория, спрямо тези в staging областта.
- С опция
—staged
(—cached
в по-стари версии) - промени на файлове в staging.
Staging областта
изтриване на файлове
- Изтрива файл от работната директория и го маркира като изтрит в staging.
Staging областта
премахване на промени
git reset new_or_modified.rb
git reset lectures/11
- Премахва направени промени в staging, като възстановява последната им commit-ната версия. По подразбиране не променя работната директория.
Типове обекти
- Файл (
blob
) - конкретна негова версия (snapshot), компресиран
- Дърво (
tree
) - списък от хешове на файлове и дървета
Commit
- за него след малко
Tag
- етикет, съдържащ идентификатора на конкретен commit
- Съхраняват се в
.git/objects
- Всеки обект се идентифицира с
sha1
хеш от съдържанието си
Commit
Конкретна версия на дърво (snapshot)
Една конкретна версия на кода.
Състои се от:
- Уникален идентификатор
- Автор и committer - може да са различни хора
- Съобщение
- Време на добавяне
- Хеш на предходния commit
- Хеш на дървото, за което се отнася
- Други метаданни
История на промените
git log
git log --stat
git log --since=2.weeks
git log --since=5.days.15.minutes
git log --until=2013-11-30
git log --grep 'Proc.new'
git log --author 'Dimitar Dimitrov'
git log lectures/01-introduction-to-ruby.slim
Commit
Да разгледаме
git show 5fae69568cd5420e13a34c06f6f495ea9bcb9a4e
git show 5fae695
git show HEAD
git show HEAD^
git show HEAD^^^
git show HEAD~3
git diff HEAD~3..HEAD
HEAD
е указател към последния commit
HEAD^^^
- Броят стрелкички е поредният номер на commit-a (0 е най-скорошният)
HEAD~n
- същото като HEAD^
с n
на брой стрелкички
Commit
Всичко е просто файл
5fae695
е част от хеша на commit-a
Commit
git commit
git commit -m "Fix a typo on slide 3"
Commit-вайте възможно най-малките, логически свързани промени, които не чупят нищо.
Commit съобщения
- Начин на комуникация с колегите
- Казват ви какво се е случило, докато ви е нямало
- Разкриват причини за бъгове
- Показват мотивацията за промяната
Commit съобщения
Най-важното нещо във всяка VCS
- Първи ред - tl;dr версия. Възможно най-кратко и описателно - до 70 символа
- Останалите редове - допълнително описание
- Как сте разрешили определен бъг
- Каква е мотивацията да направите дадена промяна
- Какво е точното съобщение за грешка, което оправяте с този commit и прочее
- Сегашно време - все едно commit-ите говорят за себе си
- Първият ред трябва да довършва изречението: "If applied, this commit will..."
Commit съобщения
Лош пример
Update Gemfile.lock
Commit съобщения
Добър пример
Update skeptic and libv8
The skeptic update showed errors in edge cases with the "spaces around
operators" restriction.
The libv8 update was because of compile problems I had with the older
version on OS X Yosemite.
Branch-ове (клони, разклонения)
- Независими разклонения на историята
- Например два фийчъра в процес на разработка
- В Git - файл, съдържащ хеш на commit
- Този commit се счита за последен в branch-a
- Нарича се връх на branch-а (branch tip)
- Първи елемент на свързан списък от commit-и
Branch-ове
Branch-ове
Особености
- Branch по подразбиране -
master
HEAD
всъщност е референция към текущия branch (файл, съдържащ името му)
- Локалните и отдалечените са различни -
master
vs origin/master
- Всъщност
origin/master
е локален branch, съответстващ на отдалечения. Ако го променим - отново трябва да push-нем
.git/refs/heads/<branch name>
Създаване на branch
git branch killer-feature
git checkout -b killer-feature
Превключване между вече създадени
git checkout killer-feature
git checkout master
Създаване на branch
Демонстрация
- Нов файл в
.git/refs/heads/
Обновяване
git pull
git pull origin
git pull origin master
- Изтегля промените и ги слива с текущия branch
Публикуване
git push
git push origin killer-feature
git push origin local-branch-name:killer-feature # Ако са с различни имена
origin
е име на отдалечено хранилище - това, от което сме клонирали
- На мястото на
origin
може да стои името на което и да е отдалечено хранилище
Сливане на разклонения
git merge killer-feature
git merge --squash killer-feature
- Слива промени от (най-често) 2 клона
- Често създава нов commit
Различни стратегии на сливане. Основните са 2:
Fast-Forward стратегия
Просто премества указателя за клона
Fast-Forward стратегия
Просто премества указателя за клона
Recursive стратегия
Слива 2 разделили се клона с обща история.
Recursive стратегия
Жълтото e merge commit-a. Той съдържа промените и от двата клона.
Обновяване 2
git pull == git fetch && git merge origin/master
- Изтегля промените и ги слива с текущия бранч
Изтриване на branch
git branch -d killer-feature
git push origin --delete killer-feature # Ако сте го push-нали
git push origin :killer-feature # В по-стари версии
- Премахва само файла на branch-a, commit-ите се пазят
- Дори ако е останал commit, който е извън историята, пак може да се възстанови
- За възстановяване - след малко
Изтриване на branch
Типичен процес на работа
Заради "евтините" branch-ове на git можем да направим следното:
- Имаме два основни branch-a -
master
и develop
(internal
, unstable
или както го кръстите)
- Когато започваме нова функционалност, правим branch специално за тази промяна
- Когато приключим, я качваме в
develop
с git merge
- В
develop
нещата се тестват и след определено време се сливат с master
- Аналогично е и
master
+ production
Типичен процес на работа
Типичен процес на работа
Резултат
- В
master
има само стабилни промени
- В
develop
има нови функции
- Ако две нови функции си пречат, това може да се поправи, преди да влязат в стабилната версия
- Ако трябва бързо да поправим нещо в стабилната версия на кода, можем директно да направим commit в
master
Машината на времето
Или как да върнем загубени промени.
git reset
git checkout
git commit --amend
git revert
git reflog
Машината на времето
git reset <commit> <files>
git reset HEAD lectures/git.slim
- Променя файлове в staging областта, като ги взима от хранилището
- В случая
HEAD
указва от кой точно commit да се вземат файловете. Там може да има и хеш на commit
- Не променя файловете в работната директория
- Използва се, когато не искате да commit-вате файл, но да запазите локалните промени
- С
--hard
променя и работната директория
Машината на времето
git checkout <files>
git checkout <commit> <files>
git checkout <branch> <files>
- Променя файлове в работната директория, като ги взима от staging областта...
- ... или от определен commit.
- Внимание! Може да изгубите код, който не е commit-нат!
Машината на времето
git commit --amend
git push --force origin branch # Ако вече сте push-нали
- Променя последния commit
- Всъщност създава нов commit със същия предшественик
- Старият commit все още може да бъде възстановен
- Удобно е за поправяне на грешки в съобщението или добавяне на изпуснати файлове
- Не е хубаво да се прави, ако вече сте push-нали, защото някой друг може да е направил pull и ще има конфликт