-
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 команди
5.13 Git инструменти - Заместване
Заместване
Както вече подчертахме, обектите в базата данни на Git са непроменими, но Git предлага интересен начин да се преструва, че замества обекти в базата си данни.
Командата replace
ви позволява да укажете един специфичен обект в Git и да кажете "всеки път, когато се обръщаме към този обект, третирай го като различен такъв".
Това най-често е полезно за заместване на един къмит в историята с друг такъв без необходимост от преправяне на цялата история с git filter branch
например.
Нека кажем, че имате обширна история за даден проект и искате да я разделите на една по-кратка част за новите разработчици и една много по-дълга за хората, които искат да изследват кода в дълбочина. Можете да присадите едната история в другата "замествайки" най-ранния къмит в новата линия с най-късния от по-старата. Това е добре, защото означава, че в действителност не трябва да пренаписвате всеки къмит в новата история, което нормално бихте направили за да ги обедините в едно (защото наследствеността засяга SHA-1 хешовете).
Нека да опитаме.
Ще вземем налично хранилище, ще го разделим в две отделни, едно актуално и едно хронологическо и след това ще видим как с replace
можем да ги комбинираме повторно без да модифицираме SHA-1 стойностите на новополучените.
Използваме хранилище с пет кратки къмита:
$ git log --oneline
ef989d8 Fifth commit
c6e1e95 Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
Искаме да разделим това на две линии история. Едната линия минава от къмити 1 до 4 - това ще ни е хронологическата линия. Втората линия ще е само с къмити 4 и 5 - това ще е актуалната история.
Създаването на хронологическата линия е лесно, просто създаваме клон до точка в историята и след това го публикуваме в 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
Така историята ни е публикувана. По-трудната част е да орежем актуалната си история, така че да стане по-кратка. Трябва ни обща пресечна точка (общ къмит), такава в която да можем да заменим къмит от едната линия с къмит в другата. Ето защо ще отрежем историята до два къмита - четвърти и пети (така че къмит 4 да е общ за двете страни).
$ git log --oneline --decorate
ef989d8 (HEAD, master) Fifth commit
c6e1e95 (history) Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
В този случай е полезно да създадем base къмит с инструкции за това как да се разшири историята, така че другите разработчици да знаят какво да правят ако срещнат първия къмит в съкратената история и се нуждаят от по-старите. Така че, това което ще направим, е да създадем начален къмит играещ ролята на изходна точка с инструкциите, след което да пребазираме останалите два къмита (четвърти и пети) върху него.
За да започнем, трябва да изберем точка за разделяне, която за нашия случай ще е в третия къмит, 9c68fdc
.
Така base къмитът ни ще бъде базиран на това дърво.
Можем да го създадем с командата commit-tree
, която просто приема дърво и ще ни върне SHA-1 хеша на един нов къмит без родители.
$ echo 'Get history from blah blah blah' | git commit-tree 9c68fdc^{tree}
622e88e9cbfbacfb75b5279245b9fb38dfea10cf
Забележка
|
Командата |
Сега имаме базов къмит и можем да пребазираме остатъка от историята ни върху него с 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
Сега пренаписахме актуалната история базирайки я на начален къмит съдържащ в себе си инструкции за това как да реконструираме цялата история, ако поискаме това. Можем да публикуваме тази история в нов проект и когато хората клонират това ново хранилище те ще видят само последните ни два къмита и базов къмит с инструкции.
Нека сега се поставим на мястото на тези хора и да видим как можем да се сдобием с пълната история на проекта. За да вземем хронологическите данни след клонирането на това орязано хранилище, трябва да добавим втора отдалечена референция към хронологическото хранилище и да изтеглим:
$ 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
с къмита, който искаме да заместим и този, с който искаме да го заместваме.
Ние искаме да заменим "fourth" къмита в master
клона с "fourth" къмита от 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
) ще работят както бихме очаквали от тях.
Обаче, историята все още показва 81a708d
като SHA-1 стойност, въпреки че реално се използват данните от 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
беше нашия placeholder къмит (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
Това означава, че е лесно да споделим подмяната си с други хора, защото можем да я публикуваме в сървъра ни и те лесно могат да я свалят. Това не е така полезно в сценария, който следвахме тук (понеже така или иначе всеки ще изтегля и двете истории и е излишно да ги разделяме), но може да е полезно в други ситуации.