1817

Как начать понимать Git1

На реддите в разделе "ProgrammerHumor" к бувально каждой шутке про git верхним, максимально заплюсованным комментом идёт ссылка на сайт https://ohshitgit.com/

Русская версия этой странички недавно была опубликована в виде поста на пикабу, что побудило меня написать данный текст. Он в первую очередь для тех, кто давно уже (месяцы или даже больше года) пользуется Git, но всё ещё регулярно копирует всю папку с проектом перед выполнением "страшных" команд. У меня этот период занял года два, а потом наконец случилось "просветление".

Причиной просветления послужил сайт think-like-a-git.net

Автор данного сайта обнаружил интересную закономерность. Люди в интернете, объясняя как работает Git, часто используют фразы наподобие: "Git становится намного понятнее, как только ты понимаешь, что...." - и далее часть фразы, которая вообще никак твоё понимание не увеличивает. То есть, существует странный понятийный разрыв - люди, которые поняли Git, не могут донести своё понимание, и это системная проблема. И тогда этот замечательный человек создал целый сайт, который коротко (относительно) и с юмором пробивает эту понятийную стену у тех, кто за много месяцев использования и чтения документации так и не пришёл в довольно очевидным вещам своим умом.

Когда я прочитал этот сайт, я сидел с открытым ртом и мыслью "Так вот оно что!". При этом, сайт не сообщает какой-то секретной новой информации. Он просто соединяет кусочки этой информации в голове читателя так, что происходит такой ощутимый щелчок, когда все части головоломки встают на свое место. И ты просто внезапно осознаешь, что фраза "git cтановится понятнее, когда ты его понял" перестаёт выглядеть для тебя бессмысленной тарабарщиной)))

Я долгое время пытался перевести этот сайт и оформить в виде поста на хабре, и у меня второй год валяется примерно десятая итерация черновика. Проблема в том, что у меня не получается сделать это короче, чем автор сайта, и при этом донести то, что он хочет донести. В итоге я отчаялся и так и не довел работу до конца, но по комментариям что на реддите, что на пикабу вижу, что проблема никуда не исчезла. Поэтому решил, что создам пост хотя-бы в таком виде.

Далее я кратко попробую сказать, о чем говорится на сайте "Think like a git", но не надейтесь что моё ультракороткое изложение может его заменить. ИДИТЕ И ЧИТАЙТЕ САЙТ.

Итак, о чем же Think-like-a-git?

Коммиты никуда не исчезают. Если вы сделали коммит - ваши данные в безопасности. Если вы в проекте из 1000 коммитов случайно сделали git reset --hard на первый коммит - вы не сделали ничего даже отдалённо ужасного. У вас есть определенная проблема - в выдаче команды git log вы видите один единственный начальный коммит, и не видите остальных коммитов, которые включают в себя (допустим) пару лет разработки. Но они там есть. Просто они недоступны. Но не в смысле "шеф, всё пропало!"-недоступны, а скорее "придется потратить полминутки, чтобы вернуть всё как было".

Представьте себе такой мысленный эксперимент. Мы берем Git-репозиторий, состоящий из файла text.txt. Копируем туда первую главу "Войны и мира", коммитим с комментарием "Глава 1". Потом берем, удаляем всё из файла, вставляем туда 2-ю главу и делаем git commit --amend, исправляя комментарий на "Глава 2". И так делаем 10 раз подряд. Команда git log покажет нам историю, состоящую из единственного сиротливого коммита, и если мы откроем файл - там одна только 10 глава. Потеряли ли мы первые девять глав? НЕТ! Они все остались в репозитории.

Можно подумать, что опция --amend переписывает последний коммит, но подумайте вот о чем. Имя коммита - это хэш от его содержимого+комментария+хэш родительского коммита. Как только вы редактируете хоть один символ и делает git commit --amend, вы получаете совершенно другой хэш. И совершенно другой коммит. Вы создаёте новый коммит каждый раз при --amend, но все предыдущие версии коммита никуда не пропадают и остаются лежать там же, рядышком, ожидая когда они вам понадобятся. И их содержимое реально (и несложно) достать обратно.

Вернемся к первому примеру, где мы сделали git reset --hard на первый коммит и "потеряли" 2 года разработки. Мы знаем, что где-то внутри репозитория есть второй коммит, который идет после первого, затем, третий, и так далее, вплоть до тысячного. Чтобы вернуть доступ ко всей истории, нам достаточно переключиться на последний коммит - и git немедленно "увидит" все предшествующие. Всё что нужно узнать - это имя, оно же хэш, последнего коммита.

А потом - прикрепить к нему ссылку с именем.

Если свести весь сайт "Think-like-a-git" к одной короткой фразе, то она будет следующей: "Ссылка обеспечивает к коммиту доступ". Создали ветку на коммите - и всё, информация никуда не денется и останется доступной. Снова вернемся ко второму примеру, с 10 главами "Войны и мира". Через git reflog получаем список всех 10 коммитов, которые находятся в репозитории. В каждом - по одной главе книги. Переключаемся на коммит, помеченный как "Глава 1", по его хэшу - git checkout %хэш_1%, и прикрепляем к нему ссылку любого типа. Можно создать ветку: git switch -c chapter_1. Можно создать тэг: git tag chapter_1

После повторения процедуры для всех десяти коммитов, мы можем перейти на ветку "master" и продолжить работу, но если нам вдруг понадобится текст глав с 1 по 9, достаточно посмотреть список веток/тегов и переключиться на нужный.

Обычно, если пользуешься Git из консоли - достаточно регулярно делать git log. Если даже случайно что-то напортачишь - достаточно пролистать историю консоли выше, скопипастить там имя нужного коммита, к которому потерян доступ, и всё починить в течение буквально нескольких секунд. И где-то на этом моменте страх что-то испортить начинает исчезать.

Проблема "запутанной рабочей копии"

Ещё одна вещь, о которой я хотел бы рассказать. Опять же, тщательно и подробно она описана вот тут (ОПЯТЬ ЖЕ, ИДИТЕ И ЧИТАЙТЕ!). Если коротко и сумбурно - опция git add --patch, или же коротко git add -p - позволяет добавлять в индекс файл "покусочно". И если вы не пробовали эту опцию - с высокой степенью вероятности, вы полюбите её с первого взгляда и больше git add без неё использовать не будете. Она прогоняет все изменения в каждом файле, кусок за куском, и спрашивает вас - "добавить этот кусок в index?". Вы просто отвечаете y/n. Очень часто это позволяет выявить (и не добавлять в коммит) ненужные вещи, вроде лишних вставок пустых строк или debug-код, который вы в процессе работы написали, но в коммите он строго говоря не нужен.

Данная опция позволяет одновременно в одном файле работать над несколькими несвязанными фичами, и в любой произвольный момент времени закоммитить одну готовую фичу, вне зависимости от готовности остальных. Или, например, в середине работы над фичей решить, что срочно надо отрефакторить названия всех функций, после чего сделать коммит с новыми названиями, и продолжить работать над фичей.

По-умолчанию, для нового файла в проекте опция --patch не работает, Git добавляет такой файл в index только целиком. Если вы хотите новый файл разбить по нескольким коммитам - необходимо сперва этот файл добавить с опцией -N, она же --intent-to-add - после чего опция --patch для него заработает.

Использование git diff / git show для быстрого включения в работу

Это просто маленький лайфхак, пишу его для тех, кто не в курсе. Допустим, вы работали над проектом, вечером в пятницу не успели ничего доделать (и в рабочем каталоге творится какая-то каша, и программа даже не компилится - ай яй яй, нехорошо так делать!).

Придя на работу в понедельник, первым делом выполняете git diff и вдумчиво листаете - перед глазами оказываются те вещи, над которыми вы работали, и ничего лишнего. Это позволяет намного быстрее включиться в работу, особенно если изменения разбросаны по разным файлам.

Второй пример. Вам надо продолжить работу над проектом после перерыва в несколько месяцев. Вы открываете проект, там несколько десятков файлов лютого, запутанного кода. Вы не понимаете, как он работает и что он делает, несмотря на то, что это ваш собственный код. git diff пишет, что рабочий каталог без изменений (к счастью!).

Первое действие - git log, и читаем комментарии к коммитам. В памяти постепено начинает всплывать, над какими функциями вы работали, когда последний раз трогали этот проект.

Второе действие - git show HEAD и читаем последний коммит. Затем git show HEAD~1 и читаем предпоследний. И ещё один. Если коммиты сделаны по всем правилам - небольшие и содержат только одно небольшое изменение - то проект читается как открытая книга. И через короткий промежуток времени в голове восстанавливается тот рабочий контекст, в котором вы вносили последние изменения в проект. Можно продолжать работать.

Как можно потерять сделанную работу?

Главная опасность - это потерять несохранённые изменения. Если у вас в рабочем каталоге накопилось изменений на кучу коммитов, в процессе разбивки кода по коммитам можно знатно обосраться и случайно сбросить несохранённые изменения. Если и есть причина перед работой с Git сделать копию проекта - то это она :) Я предпочитаю в таких случаях сделать огромный коммит со всеми изменениями в кучу, добавить ему ветку или тэг типа saved_commit, после чего сделать git reset HEAD~ (при этом все изменения снова окажутся в рабочем каталоге) и начать распихивать всё по коммитам. В случае проблем:
1) git reset --hard сбрасывает рабочий каталог (уничтожит всю сделанную работу, если не сохранить её в коммит предварительно!)
2) git checkout saved_commit заново заполняет рабочий каталог плодами многочасового труда
3) git reset master возвращает указатель на ветку master, рабочий каталог содержит все изменения из saved_commit,

Вторая опасность, о которой неоднократно писали в комментариях - это делать git push --force. Но это выходит за рамки данной статьи.

Вы смотрите срез комментариев. Показать все
4
Автор поста оценил этот комментарий
Может для понимания оно и нужно, только вот по моему долгому опыту разработки:
Если нет сабмодулей и не пользуешься stash достаточен git gui: большинство операций в нем: забрать, слить себе в лок. ветку (конфликты разрешаю в файлах, а не в git gui), добавить в индекс, закоммитить, запушить.
Поясните, когда ведется разработка, КОМУ КОГДА понадобиться что-то еще???? Как может случиться какая-то срань, если выполнять только такие команды?? Reset branch или ammend - нахера???
раскрыть ветку (22)
6
Автор поста оценил этот комментарий

Вот ситуации, с которыми сталкивался я:

1) сделал исправление, коммит-пуш. Через 5 сек понял, что забыл еще в одной строчке подправить. Тут amend приходит на помощь (как минимум если не запушил еще). Но если это ветка мастер и  ты пушишь аменд, то там вообще поломается все, ибо амендить в мастер по-умолчанию нельзя. То есть потом еще откат делать и приводить в консистентное состояние локальную и удаленную ветку.


2) вел разработку в одной ветке, нужно переключиться на другую. Забыл сделать stash, в итоге все файлы с изменениями автоматом перенеслись в другую ветку. Возвращаюсь на первичную ветку, а изменения назад уже не переносятся. То есть одно и то же действие приводит к разным результатам.


3) Нужно протестить что-то, а для этого смержить необходимо в develop или test. Понял, что изменения не работают и нужно откатить. Здесь еще засада, потому что могут выстрелить в ногу некоторые действия по реверту, когда их начнёшь пушить и заводить новые пулл реквесты. Проще скопипастить старые файлы и полностью оформить как новые коммиты.


От себя добавлю, что гит меня просто раздражает в такие моменты (когда хоть что-то сложнее add, commit, push и pull надо делатт). Это абсолютно античеловеческий инструмент, который вообще ни во что не ставит юзер экспириенс. Ощущение, что он зарождался как очень простой инструмент с парой команд, который потом на коленке расширяли и дополняли новыми командами разные люди. В итоге дало кучу команд, которые не согласуются друг с другом. На простые и довольно частые действия нередко лечением является 2-3 последователтные команды с кучей флагов. В общем, это кошмар. Когда на инструмент версионирования нужно тратить десятки, а то и сотни часов обучения и набивания шишек - это говорит лишь о том, насколько инструмент неудобен и непродуман

раскрыть ветку (9)
2
Автор поста оценил этот комментарий
если это ветка мастер и ты пушишь аменд, то там вообще поломается все

Результат amend вообще можно пушить? Вот уж не знал и сам никогда запушенные коммиты не амендил. А главное, зачем? Конечно, можно всё поломать.

Нужно протестить что-то, а для этого смержить необходимо в develop или test. Понял, что изменения не работают и нужно откатить. Здесь еще засада, потому что могут выстрелить в ногу некоторые действия по реверту, когда их начнёшь пушить и заводить новые пулл реквесты

Почему вообще для вашего личного тестирования вам требуется сделать пуш? Это не нормально. Если все будут публиковать свои изменения для проверки в общую ветку, то путаница неизбежна.

Автор поста оценил этот комментарий
1. сделай еще один коммит. Гордый что ли???
раскрыть ветку (5)
2
Автор поста оценил этот комментарий

Так и делал по итогу. В результате история коммитов примерно такая:

Add feature A

Fix feature A

Fix feature A v2

Add feature B

Hotfix feature B

Fix feature b, add more logs


Ну то есть не супер критично, но хотелось бы попроще в одно нажатие такую базовую вещь делать

раскрыть ветку (2)
Автор поста оценил этот комментарий
Но согласитесь: Вы получили такую ХЕРОВУЮ историю коммитов, подтверждающую, что Вы явно засранец, который с первого раза не может сделать фичи А, B? Если бы Вы сделали это с первого раза или, скажем, делали бы эти фичи по разным веткам, то проблем было меньше и история основной ветки была бы чище (при условии, что ветки потом с помощью rebase сливались)
раскрыть ветку (1)
4
Автор поста оценил этот комментарий

"Вы явно засранец, который с первого раза..." - и вот мы наконец приходим к причине разногласий!


В моей статье есть ссылка, приведу её ещё раз: https://tomayko.com/blog/2008/the-thing-about-git


Git не требует ничего делать "с первого раза". Вы сами можете накладывать на себя такое ограничение, но оно совершенно искусственное. Git это инструмент, просто некоторые к нему относятся с пиететом и прогибают свой рабочий процесс, а некоторые освоили и коммитят как пулемет во время работы, дополняют коммиты, переставляют местами, сливают между собой, делают по 10 раз amend и потом наводят порядок в локальной ветке и делают один раз git push, где идеальная история коммитов.

ещё комментарии
Автор поста оценил этот комментарий
3. В git gui в разделе просмотра истории коммитов можно нажать на коммите контекстное меню и сделать его revert
раскрыть ветку (1)
0
Автор поста оценил этот комментарий

А можно ли там так откатить все коммиты вплоть до нужного? Потому что как оказалось, в IDEA такое не предусмотрено (по крайней мере раньше) и по умолчанию ревертится только один коммит, даже заселектить нельзя было несколько сразу...

2
Автор поста оценил этот комментарий

Нахера amend?! Что, вы не сталкивались с ситуациями, когда

- в коммит случайно попал лишний код

- часть кода была забыта и не добавлена в коммит

- опечатка в комментарии к коммиту

ну и другие подобные ситуации...


что, никогда не было?

раскрыть ветку (11)
0
Автор поста оценил этот комментарий

- опечатка в комментарии к коммиту

Было безусловно. Только это проверяется и апрувается при merge request-е в ветке по задаче. Если нашёл в merge request-е какую то фигнюшку: комичу и комичу. Чтобы история знала своих "героев"!

0
Автор поста оценил этот комментарий

часть кода была забыта и не добавлена в коммит

Я наверное когда коммит делаю, то делаю его с явной целью: что-то добавить/изменить/удалить. КАК МОЖНО СЦУКО ЗАБЫТЬ КАКОЙТО КОД??????

раскрыть ветку (7)
1
Автор поста оценил этот комментарий

У тебя фича, разбросанная по десятку файлов. Помимо этой фичи, в этих файлах есть и другие изменения, которые относятся к другой фиче. В итоге в коммит идет примерно ~30% от того, что выдаёт git diff

раскрыть ветку (6)
0
Автор поста оценил этот комментарий
У тебя фича, разбросанная по десятку файлов.

Звучит как код с высокой связностью. Не уверен, что это хорошо и что такое вообще надо куда-то коммитить без рефактора.

раскрыть ветку (2)
0
Автор поста оценил этот комментарий

Или же звучит как "массовый рефакторинг который внезапно затронул весь проект") Обязательно надо было докопаться?

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

слово "внезапно", опять же, требует вторичного докапывания...

0
Автор поста оценил этот комментарий

Мне кажется, что вот это как раз отличный пример неправильной работы с гитом. Разные фичи разрабатываются в разных ветках, чтобы мержить можно было в разном порядке и без описанной выше чехарды с файлами. Если одна фича зависит от другой - ее ветка должна базироваться на ветке той фичи, а не на мастере/девелопе. Это полностью решает проблемы с "закоммитили лишнее" и "недокоммитили нужное". Если нужна чистота коммитов в ветках (религия или лиды запретили сквош), то можно, конечно, вместо коммита перед переключением на другую ветку совать все в стеш. Чуть менее удобно, но в итоге есть несколько стешей, в которых лежат нужные изменения для разных фич

раскрыть ветку (2)
1
Автор поста оценил этот комментарий

"Фича" это условное понятие. И мне совершенно не жалко скинуть ссылку из поста ещё раз.


https://tomayko.com/blog/2008/the-thing-about-git


"Правильно" и "неправильно" это оценочные суждения. Удобно и эффективно - вот что имеет значение. Git позволяет забыть о системе контроля версий и просто херачить код, не отвлекаясь. Бывает, что в середине работы над чем-то ты понимаешь, что тебе надо поменять имя какой-то функции везде... Потом понимаешь, что требуется допилить вон тот класс... потом - что для красивой реализации задуманного надо еще третий класс доработать... В целом - это нормальный рабочий процесс, и в нём нет ничего "неправильного". Например, в моём случае у меня может быть изменение в модели, изменение в классе, отвечающем за настройки, изменения в XML-файлах с интерфейсом, изменения с локализацией... Я просто делаю это всё вместе, а потом, когда всё заработало - я берусь за git и начинаю логически распределять по коммитам всё. Сперва всё, что связано с моделью - коммит. Потом настройки, который учитывают новую модель - коммит. Затем - файлы интерфейса, поскольку это гора XML который засоряет коммит с кодом, если их объединить... Затем - отдельно - локализацию. И после всего этого у меня в выдаче diff ещё остаётся всякий мусор типа временных дебагов и лишних переносов строк которые случайно затесались. Я весь день не трогаю гит, а потом за 20 минут все изменения очень красиво оформляю по нескольким тематическим коммитам, следят чтобы каждый из них оставлял программу в работоспособном состоянии. И потом один раз git push и работа закончена, всякие дебаги остаются в коде если я прикидываю что они мне еще понадобятся (но в коммит они не идут, просто сидят в рабочей директории)

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Фича - вполне однозначное понятие, как минимум в трекинговых системах, поэтому оно у меня за глаз и зацепилось
Ни в коем случае не претендовал на истину в последней инстанции. Если по процессу или по договоренности удобнее разбивать разработку какого-то более-менее атомарного функционала по кокретным коммитам, как вы описали - на здоровье, главное, чтобы работать было комфортно, благо гит позволяет им пользоваться как угодно. Но в таком случае вам и правда придется решать проблемы с контролем локальных изменений

По ссылке из поста описывается как раз ситуация, о которой я подумал - когда совершенно независимый друг от друга функционал оказывается в локальной версии. У вас же это один функционал, который нужно впоследствии разбить на коммиты (если я правильно понял, каюсь - по ссылке читал по диагонали). Проблемы разные, но решение одно

Автор поста оценил этот комментарий

- в коммит случайно попал лишний код

НИКОГДА: перед коммитом я проверяю его в окне изменений! Вы что там, издеваетесь надо мной что ли?

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Все проверяют, я тоже. Но бывает такое, что ты проглядел строчку которая логически относится к другому коммиту. Например, ты делаешь фичу, и разбиваешь её логически по нескольким коммитам. И вот в коммит попала строчка, которая относится к фиче, но ты хочешь чтобы эта строчка была в следующем коммите.

Вы смотрите срез комментариев. Чтобы написать комментарий, перейдите к общему списку

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества