-
1. Почетак
- 1.1 О контроли верзије
- 1.2 Кратка историја програма Гит
- 1.3 Шта је Гит?
- 1.4 Командна линија
- 1.5 Инсталирање програма Гит
- 1.6 Подешавања за први пут
- 1.7 Тражење помоћи
- 1.8 Резиме
-
2. Основе програма Гит
- 2.1 Прављење Гит репозиторијума
- 2.2 Снимање промена над репозиторијумом
- 2.3 Преглед историје комитова
- 2.4 Опозив
- 2.5 Рад са удаљеним репозиторијумима
- 2.6 Означавање
- 2.7 Гит алијаси
- 2.8 Резиме
-
3. Гранање у програму Гит
- 3.1 Укратко о гранању
- 3.2 Основе гранања и спајања
- 3.3 Управљање гранама
- 3.4 Процеси рада са гранањем
- 3.5 Удаљене гране
- 3.6 Ребазирање
- 3.7 Резиме
-
4. Гит на серверу
- 4.1 Протоколи
- 4.2 Постављање програма Гит на сервер
- 4.3 Генерисање јавног SSH кључа
- 4.4 Подешавање сервера
- 4.5 Гит демон
- 4.6 Паметан HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Опције за хостовање које нуде трећа лица
- 4.10 Резиме
-
5. Дистрибуирани Гит
-
6. GitHub
-
7. Гит алати
- 7.1 Избор ревизија
- 7.2 Интерактивно стејџовање
- 7.3 Скривање и чишћење
- 7.4 Потписивање вашег рада
- 7.5 Претрага
- 7.6 Поновно исписивање историје
- 7.7 Демистификовани ресет
- 7.8 Напредно спајање
- 7.9 Rerere
- 7.10 Отклањање грешака са програмом Git
- 7.11 Подмодули
- 7.12 Паковање
- 7.13 Замена
- 7.14 Складиште акредитива
- 7.15 Резиме
-
8. Прилагођавање програма Гит
- 8.1 Конфигурисање програма Гит
- 8.2 Гит атрибути
- 8.3 Гит куке
- 8.4 Пример полисе коју спроводи програм Гит
- 8.5 Резиме
-
9. Гит и остали системи
- 9.1 Гит као клијент
- 9.2 Мигрирање на Гит
- 9.3 Резиме
-
10. Гит изнутра
- 10.1 Водовод и порцелан
- 10.2 Гит објекти
- 10.3 Гит референце
- 10.4 Pack фајлови
- 10.5 Рефспек
- 10.6 Протоколи за пренос
- 10.7 Одржавање и опоравак податак
- 10.8 Променљиве окружења
- 10.9 Резиме
-
A1. Додатак А: Програм Гит у другим окружењима
- A1.1 Графички интерфејси
- A1.2 Гит у Visual Studio
- A1.3 Гит у Visual Studio Code
- A1.4 Гит у IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Гит у Sublime Text
- A1.6 Гит унутар Bash
- A1.7 Гит у Zsh
- A1.8 Гит у Powershell
- A1.9 Резиме
-
A2. Додатак Б: Уграђивање програма Гит у ваше апликације
- A2.1 Гит из командне линије
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Додатак В: Гит команде
- A3.1 Подешавање и конфигурација
- A3.2 Набављање и креирање пројеката
- A3.3 Основно снимање
- A3.4 Гранање и спајање
- A3.5 Дељење и ажурирање пројеката
- A3.6 Инспекција и поређење
- A3.7 Отклањање грешака
- A3.8 Крпљење
- A3.9 Имејл
- A3.10 Спољни системи
- A3.11 Администрација
- A3.12 Водоводне команде
7.13 Гит алати - Замена
Замена
Објекти програма Гит не могу да се промене, али он обезбеђује интересантан начин претварања да у својој бази података замењује са другим објектима.
Команда replace
вам омогућава да наведете објекат у програму Гит и кажете „сваки пут се обратиш овом објекту, претварај се да је то неки други објекат”.
Ово је најчешће корисно да се један комит у вашој историји замени неким другим, без потребе да са рецимо git filter-branch
морате да поново изградите комплетну историју.
На пример, рецимо да имате огромну историју кода и желите да поделите свој репозиторијум у једну кратку историју за нове програмере и једну много дужу и већу историју за људе које интересује рударење података. Једну историју можете да накалемите на другу „замењивањем” најранијег комита у новој линији са последњим комитом у старијој. Ово је лепо јер значи да нема потребе да заиста поново испишете сваки комит у новој историји, као што би то морали ако бисте их спојили заједно (јер родитељство утиче на SHA-1 суме).
Хајде да ово испробамо.
Узмимо постојећи репозиторијум, поделимо га у два репозиторијума, један скорашњи и један историјски, па ћемо видети како можемо да их рекомбинујемо без потребе за изменом SHA-1 вредности скорашњих репозиторијума помоћу replace
.
Користићемо једноставан репозиторијум са пет простих комитова:
$ git log --oneline
ef989d8 Fifth commit
c6e1e95 Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
Ово желимо да поделимо у две линије историје. Једна линија иде од комита један до комита четири - то ће бити историјска. Друга линија ће представљати само комитове четири и пет – то ће бити скорашња историја.
Дакле, креирање историје је једноставно, потребно је само да у историју поставимо грану, па да је онда гурнемо на master
грану новог удаљеног репозиторијума.
$ git branch history c6e1e95
$ git log --oneline --decorate
ef989d8 (HEAD, master) Fifth commit
c6e1e95 (history) Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
Сада нову history
грану можемо да гурнемо на master
грану нашег новог репозиторијума:
$ git remote add project-history https://github.com/schacon/project-history
$ git push project-history history:master
Counting objects: 12, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (12/12), 907 bytes, done.
Total 12 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (12/12), done.
To git@github.com:schacon/project-history.git
* [new branch] history -> master
OK, објавили смо своју историју. Сада долази тежи део одсецања скорашње историје, тако да постане мања. Потребно нам је преклапање тако да комит у једној заменимо еквивалентним комитом у другој, тако да ћемо ово скратити само на комитове четири и пет (дакле, преклапа се комит четири).
$ git log --oneline --decorate
ef989d8 (HEAD, master) Fifth commit
c6e1e95 (history) Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
У овом случају је корисно да се креира базни комит који садржи упутства како да се историја прошири, тако да остали програмери знају шта да раде ако дођу до првог комита у скраћеној историји, а потребно им је још. Дакле, оно што ћемо урадити је креирамо објекат почетног комита као базну тачку са упутствима, па да затим ребазирамо преостале комитове (четири и пет) преко њега.
Да би то постигли, морамо да изаберемо тачку поделе, што је у нашем случају трећи комит, 9c68fdc
у SHA говору.
Значи, наш базни комит ће се базирати од тог трећег.
Базни комит можемо креирати употребом commit-tree
команде која једноставно узима стабло и враћа нам SHA-1 потпуно новог комит објекта који нема родитеља.
$ echo 'get history from blah blah blah' | git commit-tree 9c68fdc^{tree}
622e88e9cbfbacfb75b5279245b9fb38dfea10cf
Белешка
|
Команда |
OK, сада када имамо базни комит, можемо да ребазирамо остатак наше историје преко њега са git rebase --onto
.
Аргумент --onto
ће бити SHA-1 који смо управо добило од команде commit-tree
и тачка ребазирања ће бити трећи комит (родитељ првог комита који желимо да задржимо, 9c68fdc
):
$ git rebase --onto 622e88 9c68fdc
First, rewinding head to replay your work on top of it...
Applying: Fourth commit
Applying: Fifth commit
OK, сада смо поново исписали нашу скорашњу историју преко базног комита за одбацивање који сада у себи има упутство како да се у случају потребе реконституише комплетна историја. Ту нову историју можемо да гурнемо у нови пројекат, па када онда људи клонирају тај репозиторијум, видеће само последња два комита, а онда и базни комит са упутством.
Хајде да сада заменимо улоге и постанемо неко ко по први пут клонира пројекат и жели комплетну историју. Да би добио податке о историји након клонирања овог скраћеног репозиторијума, он би требало да дода још један удаљени репозиторијум за онај који чува комплетну историју и да преузме одатле:
$ git clone https://github.com/schacon/project
$ cd project
$ git log --oneline master
e146b5f Fifth commit
81a708d Fourth commit
622e88e Get history from blah blah blah
$ git remote add project-history https://github.com/schacon/project-history
$ git fetch project-history
From https://github.com/schacon/project-history
* [new branch] master -> project-history/master
Сарадник би сада у master
грани имао последње комитове, а у project-history/master
грани историјске комитове.
$ git log --oneline master
e146b5f Fifth commit
81a708d Fourth commit
622e88e Get history from blah blah blah
$ git log --oneline project-history/master
c6e1e95 Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
Ако желите да их комбинујете, можете једноставно да позовете git replace
са комитом који желите замените, па са комитом којим желите да га замените.
Тако да „четврти” комит у master
грани желимо да заменимо са „четвртим” комитом у project-history/master
грани:
$ git replace 81a708d c6e1e95
Ако сада погледате историју master
гране, изгледаће овако:
$ git log --oneline master
e146b5f Fifth commit
81a708d Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
Фино, зар не? Били смо у могућности да заменимо један комит у нашој историји потпуно другим комитом, без потребе да мењамо све SHA-1 контролне суме узводно, а сви уобичајени алати (bisect
, blame
, итд.) ће радити онако како се и очекује да раде.
Интересантно је да се као SHA-1 сума још увек приказује 81a708d
, мада се уствари користе подаци c6e1e95
комита којим смо га заменили.
Чак и ако извршите команду као што је cat-file
, она ће вам приказати замењене податке:
$ git cat-file -p 81a708d
tree 7bc544cf438903b65ca9104a1e30345eee6c083d
parent 9c68fdceee073230f19ebb8b5e7fc71b479c0252
author Scott Chacon <schacon@gmail.com> 1268712581 -0700
committer Scott Chacon <schacon@gmail.com> 1268712581 -0700
fourth commit
Упамтите да је стварни родитељ комита 81a708d
наш комит чувар места (622e88e
), а не 9c68fdce
као што овде пише.
Још једна интересантна ствар је да се ови подаци чувају у нашим референцама:
$ git for-each-ref
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/heads/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit refs/remotes/history/master
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/remotes/origin/HEAD
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/remotes/origin/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit refs/replace/81a708dd0e167a3f691541c7a6463343bc457040
То значи да је дељење наше замене са осталима једноставно јер ово можемо да гурнемо на наш сервер и остали то лако могу да преузму. Ово није од велике помоћи у сценарију калемљенња историје који смо овде преставили (пошто би се у сваком случају преузимале обе историје, па зашто да се онда раздвајају?), али може бити корисно у неким другим околностима.