-
1. Начало
- 1.1 За Version Control системите
- 1.2 Кратка история на Git
- 1.3 Какво е Git
- 1.4 Конзолата на Git
- 1.5 Инсталиране на Git
- 1.6 Първоначална настройка на Git
- 1.7 Помощна информация в Git
- 1.8 Обобщение
-
2. Основи на Git
-
3. Клонове в Git
-
4. GitHub
-
5. Git инструменти
- 5.1 Избор на къмити
- 5.2 Интерактивно индексиране
- 5.3 Stashing и Cleaning
- 5.4 Подписване на вашата работа
- 5.5 Търсене
- 5.6 Манипулация на историята
- 5.7 Мистерията на командата Reset
- 5.8 Сливане за напреднали
- 5.9 Rerere
- 5.10 Дебъгване с Git
- 5.11 Подмодули
- 5.12 Пакети в Git (Bundling)
- 5.13 Заместване
- 5.14 Credential Storage система
- 5.15 Обобщение
-
6. Настройване на Git
- 6.1 Git конфигурации
- 6.2 Git атрибути
- 6.3 Git Hooks
- 6.4 Примерна Git-Enforced политика
- 6.5 Обобщение
-
7. Git и други системи
- 7.1 Git като клиент
- 7.2 Миграция към Git
- 7.3 Обобщение
-
8. Git на ниско ниво
- 8.1 Plumbing и Porcelain команди
- 8.2 Git обекти
- 8.3 Git референции
- 8.4 Packfiles
- 8.5 Refspec спецификации
- 8.6 Транспортни протоколи
- 8.7 Поддръжка и възстановяване на данни
- 8.8 Environment променливи
- 8.9 Обобщение
-
9. Приложение A: Git в други среди
-
10. Приложение B: Вграждане на Git в приложения
- 10.1 Git от команден ред
- 10.2 Libgit2
- 10.3 JGit
- 10.4 go-git
- 10.5 Dulwich
-
A1. Приложение C: Git команди
- A1.1 Настройки и конфигурация
- A1.2 Издърпване и създаване на проекти
- A1.3 Snapshotting
- A1.4 Клонове и сливане
- A1.5 Споделяне и обновяване на проекти
- A1.6 Инспекция и сравнение
- A1.7 Дебъгване
- A1.8 Patching
- A1.9 Email команди
- A1.10 Външни системи
- A1.11 Административни команди
- A1.12 Plumbing команди
2.2 Основи на Git - Запис на промени в хранилището
Запис на промени в хранилището
Вече имате чисто ново Git хранилище и работещо копие на файловете от проекта. Сега трябва да започнете да правите промените, които желаете и да записвате snapshot-и на промените в хранилището всеки път, когато проектът ви достигне състояние, което бихте желали да запишете на сигурно място.
Помнете, че всеки файл от работната ви директория може да бъде в два статуса - проследяван или не (tracked/untracked). Tracked файловете са тези от последния snapshot, както и всички новоиндексирани файлове; те може да са непроменени, променени и индексирани (staged). Накратко, tracked файловете са тези, които Git познава.
Untracked файловете са всичко останало - всички файлове в работната ви директория, които не са били в последния ви snapshot и не са в staging областта. Когато за пръв път клонирате хранилище, всички ваши файлове ще бъдат tracked и същевременно - unmodified, защото Git току що ги е извлякъл и вие все още не сте променяли нищо по тях.
Когато започнете да променяте файловете, Git ги вижда вече като modified, понеже сте ги редактирали след последния къмит. Вие индексирате тези променени файлове, след това къмитвате промените им и този цикъл се повтаря в течение на работата ви.
Проверка на статуса на файловете
Основният инструмент, с който се проверява състоянието на файловете ви е командата git status
.
Ако я изпълните директно след клониране, когато не сте правили промени все още, ще видите следното:
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
Това означава, че имате чиста работна директория - с други думи, никой от следените ви файлове не е променян.
Git също така не вижда никакви непроследени файлове, иначе щеше да ги покаже тук.
Накрая, командата ви казва в кой клон (branch) на проекта се намирате и че не се отклонявате от същия клон на сървъра.
Засега, този клон е винаги master
, както е по подразбиране, към момента това не ви интересува.
Клонове в Git ще разгледа клоновете и референциите в подробности.
Забележка
|
GitHub смени името на клона по подразбиране от Git обаче все още използва |
Нека сега добавим нов файл в проекта, прост README
файл.
Ако файлът не е съществувал преди и изпълните git status
, ще видите untracked файла си така:
$ echo 'My Project' > README
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
README
nothing added to commit but untracked files present (use "git add" to track)
Можете да видите, че новият README
файл е непроследен, защото е в секцията “Untracked files” на изхода от командата.
Untracked означава, че Git вижда файл, който не е присъствал в предишния snapshot (commit) и все още не е индексиран; Git няма сам да започне да го прибавя към следващите commits докато вие не укажете това изрично.
Това е умишлено поведение и ви предпазва от ситуации, в които бихте могли автоматично да добавяте файлове, които не желаете, например генерирани binary файлове.
Вие обаче искате да включите README
файла, така че нека го направим.
Проследяване на нови файлове
За да започнете да следите нов файл, използвайте командата git add
.
За вашия README
файл, изпълнете това:
$ git add README
Ако след това изпълните отново статус командата, ще видите че README
файлът вече се следи и е индексиран за включване в следващия къмит:
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: README
Разбирате, че файлът е индексиран, защото се намира в секцията със заглавие “Changes to be committed”.
Ако къмитнете в този момент, файлът който ще попадне в следващия snapshot-а ще e в състоянието, в което е бил, когато сте изпълнили git add
командата за него.
Може да си спомните, че когато по-рано изпълнихте git init
, след това изпълнихте и git add <files>
— това беше за да започнете да следите файлове във вашата директория.
Командата git add
приема име на път за файл или директория, ако е директория - тя добавя всички файлове в нея рекурсивно.
Индексиране на променени файлове
Нека променим файл, който вече се проследява.
Ако промените вече проследен файл с име CONTRIBUTING.md
и след това изпълните git status
отново, ще видите нещо подобно:
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: README
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: CONTRIBUTING.md
Файлът CONTRIBUTING.md
се появява под секцията “Changes not staged for commit” — което значи, че проследеният файл е бил променен в работната директория, но все още не е индексиран за къмитване.
За да го индексирате - изпълнете командата git add
.
Както вече виждате, git add
е многоцелева команда — използвате я както за да започнете да следите файлове, така и за да ги индексирате в staging областта и дори да правите по-различни неща, като например да маркирате отбелязани като конфликтни по време на сливане файлове като коректни такива.
Би могло да ви е от полза да приемате значението ѝ повече като “добави това съдържание в следващия къмит” вместо като “добави този файл към проекта”.
Нека сега изпълним git add
за да индексираме файла CONTRIBUTING.md
, след което да пуснем git status
отново:
$ git add CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: README
modified: CONTRIBUTING.md
И двата файла сега са индексирани и ще попаднат в следващия къмит.
В този момент, представете си, че сте забравили да направите една дребна промяна по CONTRIBUTING.md
преди да го публикувате.
Вие го отваряте отново, правите промяната и сте готови на къмитнете.
Обаче, нека пуснем git status
още един път:
$ vim CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: README
modified: CONTRIBUTING.md
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: CONTRIBUTING.md
И какво виждаме?
Сега CONTRIBUTING.md
се показва едновременно като staged и unstaged.
Как е възможно това?
Оказва се, че Git индексира файла точно както е бил, когато сте изпълнили git add
.
Ако къмитнете сега, версията на CONTRIBUTING.md
, която ще отиде в snapshot-а ще е тази, след която сте изпълнили git add
- а не тази в която е, когато изпълните git commit
. С други думи - вашата малка промяна няма да бъде включена и публикувана.
Ако промените файл след като сте пуснали командата git add
, трябва да изпълните git add
отново, ако желаете да индексирате новата промяна:
$ git add CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: README
modified: CONTRIBUTING.md
Кратък статус
git status
информацията е доста изчерпателна, но и многословна.
Git поддържа и флаг за кратък статус, така че да виждате промените си в по-компактна форма.
Ако изпълните git status -s
или git status --short
, получавате по-опростен изход:
$ git status -s
M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt
Новите, непроследени файлове са със знака ??
, новите индексирани файлове с A
, променените файлове с M
и т.н.
Изходът е в две колони — лявата показва статуса на staging областта, а дясната статуса на работната директория.
Така в горния пример, README
файлът е променен в работната област, но не е индексиран, докато файлът lib/simplegit.rb
е променен и индексиран.
Файлът Rakefile
е променен, индексиран и след това променен отново, така че по него има промени които са индексирани и такива, които не са.
Игнориране на файлове
Често, ще имате класове от файлове, за които няма да искате Git да ги добавя автоматично и дори да ви ги показва като непроследени.
Това обикновено са автоматично генерирани файлове - лог-файлове или такива създадени от компилиращата ви система.
В подобни случаи, можете да създадете файл с име .gitignore
, в който да ги опишете с подходяща маска за имената им.
Ето един примерен такъв файл:
$ cat .gitignore
*.[oa]
*~
Първият ред в него указва на Git да пропуска всички файлове завършващи на “.o” или “.a” — обектни и архивни файлове, които може да са създадени от компилатора.
Вторият ред указва да се пропускат всички файлове, чиито имена завършват с тилда (~
), които се ползват в много текстови редактори като Emacs за маркиране на временни файлове.
Можете също да включвате log, tmp, или pid директории, автоматично генерирана документация и т.н.
Добра идея е да си направите .gitignore
файла преди да започнете работа, така че да не къмитнете без да искате нежелани файлове.
Правилата за маските, които можете да включвате в .gitignore
файла са както следва:
-
Празните редове и редовете започащи с
\#
се игнорират. -
Работят стандартните глобални правила за маски и те ще бъдат приложени рекурсивно по цялото работно дърво.
-
Можете да започвате маските с (
/
) за да избегнете рекурсия. -
Можете да завършвате маските с (
/
) за да указвате директория. -
Можете да обърнете логиката на маската като я започнете с (
!
).
Глобалните правила са подобни на опростени регулярни изрази, които шеловете използват.
Звездичката (*
) търси за нула или повече символа; [abc]
търси за кой да е символ в скобите (в този случай a, b, или c); въпросителният знак (?
) търси единичен символ; символи в скоби с тире между тях ([0-9]
) търсят за произволен символ в обхвата между символите (в този случай от 0 до 9).
Можете да използвате две звездички за да търсите в под-директории; a/**/z
ще открие a/z
, a/b/z
, a/b/c/z
, и т.н.
Ето друг примерен .gitignore
файл:
# без .a файлове
*.a
# но lib.a се включва, въпреки че игнорирате всички .а файлове отгоре
!lib.a
# игнорирай само TODO файла в текущата директория, не и под-директориите съдържащи TODO
/TODO
# игнорира всички файлове в коя да е директория с име build
build/
# игнорира doc/notes.txt, но не и doc/server/arch.txt
doc/*.txt
# игнорира всички .pdf файлове в директорията doc/ и всички нейни под-директории
doc/**/*.pdf
Подсказка
|
GitHub поддържа сравнително подробен списък от добри |
Забележка
|
В общия случай, едно хранилище би могло да има единичен Извън темата на тази книга е да се впускаме в детайли за множеството |
Преглед на индексираните и неиндексирани промени
Ако командата git status
е твърде неясна за вас (понеже може да искате да знаете точно какво сте променили, а не само имената на файловете), можете да ползвате командата git diff
.
Ще разгледаме по-подробно git diff
по-късно, вие вероятно най-често ще я ползвате за отговор на два въпроса: Какво сте променили, но не сте индексирали все още?
Какво сте индексирали и предстои да къмитнете?
Въпреки, че git status
в общи линии отговаря показвайки ви имената на файловете, git diff
показва точните редове код добавени и премахнати — пачът какъвто точно е бил.
Да кажем, че редактирате и индексирате README
файла отново и след това редактирате CONTRIBUTING.md
без да го индексирате.
Ако пуснете git status
командата, вие виждате нещо такова:
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: README
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: CONTRIBUTING.md
За да видите какво сте променили, но не индексирали - напишете git diff
без аргументи:
$ git diff
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8ebb991..643e24f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -65,7 +65,8 @@ branch directly, things can get messy.
Please include a nice description of your changes when you submit your PR;
if we have to read the whole diff to figure out why you're contributing
in the first place, you're less likely to get feedback and have your change
-merged in.
+merged in. Also, split your changes into comprehensive chunks if your patch is
+longer than a dozen lines.
If you are starting to work on a particular area, feel free to submit a PR
that highlights your work in progress (and note in the PR title that it's
Командата сравнява наличното в работната директория с това в индексната област. Резултатът ви показва промените, които са направени, но не са индексирани.
Ако желаете да видите какво сте индексирали и ще отиде в следващия къмит, можете да използвате git diff --staged
.
Това сравнява индексираните промени с това, което е било в последния къмит:
$ git diff --staged
diff --git a/README b/README
new file mode 100644
index 0000000..03902a1
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+My Project
Важно е да запомните, че git diff
сама по себе си не показва всички промени от последния къмит — а само тези, които все още не са индексирани.
Това може да е смущаващо, защото значи, че ако сте индексирали всичките си промени, git diff
няма да покаже нищо.
Друг пример, ако индексирате файла CONTRIBUTING.md
и след това го промените, можете да ползвате git diff
за да видите промените във файла, които са индексирани и тези които не са.
Ако състоянието ни изглежда така:
$ git add CONTRIBUTING.md
$ echo '# test line' >> CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: CONTRIBUTING.md
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: CONTRIBUTING.md
Сега можете да ползвате git diff
за да видите какво все още не е индексирано:
$ git diff
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 643e24f..87f08c8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -119,3 +119,4 @@ at the
## Starter Projects
See our [projects list](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md).
+# test line
и git diff --cached
за да видите файла в индексираното му състояние (--staged
и --cached
са синоними):
$ git diff --cached
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8ebb991..643e24f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -65,7 +65,8 @@ branch directly, things can get messy.
Please include a nice description of your changes when you submit your PR;
if we have to read the whole diff to figure out why you're contributing
in the first place, you're less likely to get feedback and have your change
-merged in.
+merged in. Also, split your changes into comprehensive chunks if your patch is
+longer than a dozen lines.
If you are starting to work on a particular area, feel free to submit a PR
that highlights your work in progress (and note in the PR title that it's
Забележка
|
Git Diff във външен инструмент
Изпълнете |
Публикуване на промените (commit)
Сега, след като индексната област е в състоянието, което искате, можете да публикувате (къмитнете) вашите промени.
Помнете, че всичко, което все още не е индексирано — всякакви файлове, които сте създали или редактирали след последната git add
команда — няма да отидат в това публикуване.
Те ще останат като променени файлове на вашия диск.
Нека кажем, че последния път когато сте пуснали git status
, вие сте видели, че всичко е индексирано и сте готови да къмитнете промените.
Най-простият начин да запишете е изпълнявайки командата git commit
:
$ git commit
Правейки това, Git ще стартира вашия текстов редактор.
Забележка
|
(Това се определя от |
Редакторът показва следното (в случая екранът е от Vim):
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Your branch is up-to-date with 'origin/master'.
#
# Changes to be committed:
# new file: README
# modified: CONTRIBUTING.md
#
~
~
~
".git/COMMIT_EDITMSG" 9L, 283C
Можете да видите, че подразбиращото се къмит съобщение съдържа последния изход от командата git status
в коментиран вид и един празен ред над него.
Можете да изтриете тези коментари и да напишете собствено съобщение или да ги оставите там за да ви припомнят по-късно какво точно сте публикували.
Забележка
|
Ако искате още по-подробно напомняне за това какво сте модифицирали, можете да изпълните командата с параметър |
Когато излезете от редактора запазвайки промените, Git ще публикува промените заедно със съобщението (коментарите и diff информацията се премахват)
Вместо да пускате текстовия редактор, можете да подадете къмит съобщението директно като параметър на командата с флага -m
:
$ git commit -m "Story 182: fix benchmarks for speed"
[master 463dc4f] Story 182: fix benchmarks for speed
2 files changed, 2 insertions(+)
create mode 100644 README
Сега вече къмитнахте за пръв път промените си!
Можете да видите, че това действие ви дава и допълнителна информация за себе си: към кой клон сте къмитнали (master
), каква е SHA-1 чексумата на къмита (463dc4f
), колко на брой файлове са променени и статистика за добавените и премахнати редове код.
Помнете, че къмитът съдържа моментна снимка на това, което е имало в индексната област (staging area). Всичко, което не е било там няма да присъства в къмита и файловете ще си стоят като променени. За да ги добавите - трябва да направите следващ къмит. Всеки път, когато къмитвате промени, вие правите snapshot на състоянието на вашия проект и по-късно можете да го възстановите или да го ползвате за сравнение.
Прескачане на Staging областта
Въпреки, че може да е много полезна за фина настройка на вашите промени, понякога индексната област може да се прескочи в процеса на работа.
Ако искате директно да къмитнете променени файлове без да ги добавяте в нея, Git осигурява средство за това.
Опцията -a
към командата git commit
прави така, че Git автоматично ще индексира всеки следящ се файл преди да направи къмита и така можете да пропуснете понякога досадната необходимост да изпълнявате git add
:
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: CONTRIBUTING.md
no changes added to commit (use "git add" and/or "git commit -a")
$ git commit -a -m 'Add new benchmarks'
[master 83e38c7] Add new benchmarks
1 file changed, 5 insertions(+), 0 deletions(-)
Забележете, че сега не беше нужно да изпълнявате git add
за файла CONTRIBUTING.md
преди да го къмитнете.
Това е, защото -a
параметърът включва всички променени файлове.
Това е удобно, наистина, но бъдете внимателни, понякога този флаг може да включи в къмита нежелани промени.
Изваждане на файлове
За да извадите файл от Git, вие трябва да го изключите от списъка със следящи се файлове (по-прецизно казано, да го премахнете от индексната област) и след това да публикувате промяната.
Командата git rm
прави това и също така изтрива файла от работната директория, така че да не го виждате като непроследен файл следващия път.
Ако просто изтриете файла от работната си директория, той се показва под “Changes not staged for commit” (тоест, unstaged) секцията от изхода на git status
:
$ rm PROJECTS.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: PROJECTS.md
no changes added to commit (use "git add" and/or "git commit -a")
След това, ако изпълните git rm
, системата индексира това изтриване на файла:
$ git rm PROJECTS.md
rm 'PROJECTS.md'
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: PROJECTS.md
При следващия къмит файлът ще изчезне и повече няма да се следи.
Ако сте променили файла и вече сте го добавили към индекса, трябва да форсирате изтриването с параметъра f
.
Това е предпазна опция за да предотвратите случайно изтриване на данни, които не са били публикувани и не могат да се възстановят от Git.
Друго полезно действие, което вероятно ще искате да можете да правите, е да пазите файла в работната директория, но да го извадите от индекса.
С други думи, да пазите файла на диска си, но Git да не го следи повече.
Това е особено полезно, когато сте забравили да добавите нещо в .gitignore
файла си и без да искате сте го индексирали - например голям лог-файл или купчина от .a
файлове създадени от компилатора.
За да се справите с това, ползвайте опцията --cached
:
$ git rm --cached README
Командата git rm
може да се ползва с имена на файлове, директории и цели маски за имена.
Това означава, че можете да правите подобни неща:
$ git rm log/\*.log
Отбележете обратния слеш (\
) преди звездичката \*
.
Това е нужно, защото Git прави своя собствена развивка на имената на файлове в допълнение към развивката, която прави шела.
Тази команда премахва всички файлове с разширение .log
намиращи се в директорията log/
.
Можете да направите и следното:
$ git rm \*~
Тази команда премахва всички файлове, имената на които завършват със символа ~
.
Преименуване на файлове
За разлика от много други VCS системи, Git не следи експлицитно преименуването на файлове. Ако преименувате файл в Git, никакви мета данни няма да се съхранят в Git базата, така че да му указва, че сте преименували файла. Обаче, Git е достатъчно интелигентен за да усети това — ще се занимаем с детекцията на преименуваните файлове малко по-късно.
Затова е малко смущаващо, че Git всъщност има mv
команда.
Ако искате да преименувате файл в Git, можете да изпълните това:
$ git mv file_from file_to
и то си работи. На практика, ако изпълните командата и погледнете в статуса, ще видите че Git гледа на файла като на преименуван:
$ git mv README.md README
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: README.md -> README
Обаче, това е еквивалентно на следното:
$ mv README.md README
$ git rm README.md
$ git add README
Git установява, че това е безусловно преименуване, така че няма значение дали сте променили файла по този начин или с mv
командата.
Единствената реална разлика е, че git mv
е една команда вместо три — така че това е команда за удобство.
По-важното е, че можете да използвате произволни средства за преименуване на файлове и да се занимавате с add
/rm
действията по-късно, преди да къмитнете промените.