Открыть список
Как стать автором
Обновить

Комментарии 55

Если честно — понятнее не стало

первопричина путаницы — интерпретация коммитов как различий, которые можно перетасовать. Однако коммиты — это не различия, а снимки

тогда коммиты — это снимки, которые можно перетасовать. Ещё непонятнее. Снимки как набор каких-то состояний/параметров, у разных снимков они разные и будут = то есть всё в итоге свелось к diff.

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

Самый простой пример — стандартный флоу в гите:
git add .
git commit -m 'poyehali'
git push 

То есть, мы берём файлы, которые хотим залить, помещаем их в такой себе буфер с сообщением «вот эти файлы с изменениями для задачи такой-то» и затем это всё отправляем на удалённую ветку — чтобы там эти файлы тоже сталы доступны.
И вот после этого мы поняли, что закоммитили и запушили ошибочно — что нужно сделать, чтобы вернуть всё, как было? Мы не можем написать что-то вроде 'git origin revert && git last_commit revert' и быть счастливы, нам нужно разбираться, что мы сделали и как это вернуть, и каким способом.
Например, можно вернуть все изменения в файлах, закоммитить верные и запушить «верный вариант». А что, если изменений было много, например, в 30 местах? И мы закоммитили, например, 20? Нужно перебрать все 20 мест, но это ещё нужно понимать, какие, вернуть там всё, собрать всё до кучи перед коммитом, проверить и запушить — всё это время в удалённой ветке лежит «плохой» код.
То есть, быстро отменить мы не можем, а чтобы исправить ситуацию, нам нужно или подготовить новый верный коммит+пуш, либо грохнуть последний пуш на удалённую ветку — при этом пропадают наши 20 изменений. А в попытке восстановить какое-то эталонное состояние ветки мы натыкаемся на то, что для переключения ветки нам нужно оставшиеся 10 изменений куда-то положить, в какой-то стеш.
После этого ещё стоит вспомнить, что у каждого коммита есть хеши, а у ветки есть голова, которая HEAD, поведение которой супернеинтуитивно — вот есть у нас ветка, в которой 5 коммитов. И мы можем передвинуть «голову» с 5 на 4ый, или на 3ий, или на 2й, мы можем. А что это даёт? Можем ли мы туда коммитить/пушить? И как тогда передвинуть голову обратно? Что произойдёт с коммитами?
При этом некоторые операции имеют в побочных эффектах какие-то вот эти операции с «головой», при этом у нас есть хеши коммитов и последний коммит — какие выгоды дают операции с «головой»? Понадобится какое-то время почитать, чтобы понять, что это за «голова» и почему есть reset с её участием и без.

Я попытался смоделировать самые обычные ситуации, которые возникали у меня и моих собратов по цеху, особенно у новичков, у джунов. У нас не было проблемы в понимании, что такое ветка, коммит, снимок, блоб — у нас проблема в том, что на любую нестандартную ситуацию нужно идти на СтэкОверФлоу и читать, как посмотреть логи, как посмотреть, что ты сделал, как посмотреть, что у тебя было, как посмотреть, что у тебя стало и как из этого сделать то, что тебе нужно, не потеряв того, что тебе нужно, и всё это желательно в флоу разработки, когда ветка твоей фичи ответвлялась от ветки «develop» 2 недели назад, а заливать на «тестовый сервер» нужно уже вот сейчас/через час/сегодня — и у тебя там ещё 5 минут назад всё было хорошо и понятно.

п.с: а как гит отслеживает переименования — честно, это знание поможет мизерно. Понимание работы карбюратора мало поможет в навыках управлением автомобилем, по крайней мере видятся какие-то такие метафоры

Ну, если rebase разрешен, то починить описанную ситуацию с неверным коммитом просто. Сташим свою работу, ресетим ветку на коммит назад и push --force.


Затем возвращаем ветку локально к своему первоначальному коммиту, вытаскиваем работу из стеша и работаем дальше, как ни в чем не бывало.


В нормальном GUI клиенте это все за несколько секунд делается. В консольке тоже все довольно просто, даже для новичка (наверное самое сложное — откатится на коммит назад, а потом вернуться вперед, т.к. это нужно представлять дерево в голове).




А вообще, конечно, незачем пушить работу, если не уверен. Работай локально, полируй, ребейзь, жди ответного звонка и проталкивай, когда все готово.

«когда все готово» — ну такое, очень расплывчатое понятие. Бывает ли такое в жизни вообще?

Тут на усмотрение разработчика. У меня "все готово" — когда каждый коммит делает ровно одну вещь, описан и очевиден. А в процессе работы это часто не так — бывает и куча разрозненных вещей в одном коммите, кучи коммитов-фиксов, пока все не устаканится. Но это остается только у меня в локальной истории. Фактически, 1/4 времени — написание кода и 3/4 — пара десятков перебазирований, чтобы сделать нормальную историю.

1/4 времени — написание кода и 3/4 — пара десятков перебазирований, чтобы сделать нормальную историю
при таком соотношении (1:3) затраченного времени на код и на поддержание истории изменений, возможно, стоит вообще отказаться от системы контроля версий. А то как-то дорого обходится.

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

А зачем пустую папку комитить?
Ну а для версионирования сабмодули можно использовать.

А зачем пустую папку комитить?

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

Радикально. Если папка необходима в коде приложения, то можно создавать динамически:


if (Directory.Exists(path) {
  Directory.Create(path);
}

Если во время инициализации или иных операций, не связанных с кодом, то через bash-команду.


Зачем гит-то заполнять пустыми папками?

Русский форум какой-то. Говоришь «я хочу так», тебе отвечают «тебе это не нужно». Нужно. Я хочу так, так мне удобнее работать.

Банальный пример — создаю папку для подключаемых модулей и пишу коллегам «с этого времени подключаемые модули кладутся в эту папку». Можно ли сделать по-другому? Можно. Можно положить туда заготовку модуля. Можно ридми. Можно написать «создайте папку, если хотите добавить модуль». Но я хочу так, а мне это запрещают сделать.

Еще один пример — заранее созданная структура, в которой договора, например, клиентов-юрлиц раскладываются по папкам. Вот я создал десять папок, и в пять из них положил договора. А остальные пять я создать не могу? Но у меня есть сущность — клиенты, в логической структуре репозитария ей соответствует сущность «папка». Которую я не могу создать.

С одной стороны вы, конечно, правы, инструмент по хорошему должен позволять делать так, как удобнее работать. Но с другой стороны, должен ли позволять багтрекер, например, создавать тикет без заголовка и описания? Кому-то это может быть удобно, но в общем случае это вредно.
С договорами для клиентов все, имхо, просто: появился клиент, сложили шаблон с реквизитами юрлица. И папка не пустая, и смысл в ней появился.

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

Вы опять пытаетесь решить проблему, которая служит для иллюстрации, что требования принципиально возникнуть могут. Я могу выдвинуть вам дополнительные уточнения (это же легко, ситуация изначально придумана), которые объяснят, почему туда нельзя складывать шаблоны, а вы можете в ответ придумать новое решение проблемы. Собственно, проблема в этом случае решается файлом .gitkeep, в других — действительно созданием папки при работы программы или иначе.

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

И сама проблема не так важна, на самом деле. Гораздо хуже то, что гит делает это совершенно тихо — созданная папка просто не попадет в репозитарий, без генерирования ошибки и тем более вопроса «вы создали папку, но она не будет сохранена в коммите, хотите создать в ней файл .gitkeep?».
Разработчик, неосведомленный об этой особенности, легко может заложить мину замедленного действия в проект, полагаясь на то, что папка всегда есть, и не делая дополнительных проверок на ее наличие. И в какой-то момент, когда из папки исчезнут файлы, она просто не создастся на клиенте при выполнении git clone, что сломает программу.

Это произойдет не потому, что разработчик плохой, а потому что люди, разрабатывающие гит, сначала допустили архитектурную проблему при проектировании, а потом не смогли обмазать ее достаточным слоем UI, чтобы она хотя бы не била так больно пользователей по пальцам, потому что отказываются признать совершенную ошибку, ссылаясь на «ну вы просто не делайте так, и проблем не будет».

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

Простите, но ваш пример очень странный, я не знаю ни одного разработчика, который не делает перед комитом git status или аналог, чтоб посмотреть, что пойдет в комит. Поэтому как можно подумать, что папка ушла в репу, когда она при этом не ушла? Я просто исхожу из опыта (это конечно, не очень сильный аргумент), но за много лет ни разу не встречал ситуации, когда нужна была пустая папка (проекты c++/Java/go). У вас был не гипотетический, а реальный случай, когда она понадобилась?

я не знаю ни одного разработчика, который не делает перед комитом git status или аналог, чтоб посмотреть, что пойдет в комит.

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

Во-первых, проблема может возникнуть не при коммите добавления. Я создал папку, добавил туда пять файлов, спустя полгода эти файлы в ходе переработки проекта начали удаляться. Удаляется последний файл — папка пропадает и при git clone не создается. Каждый коммит абсолютно корректен, но в результате поведение нерасчетное и неожидаемое.

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

Из реального случая, когда я на это напоролся — генератор документации сломался, потому что у него была инструкция «включить все файлы из этой папки», и он нормально относится к отсутствию файлов, а вот к отсутствию папки нет. Локально все ок, потому что папка-то была в фс у меня, а вот при клонировании она исчезла.

В-третьих, вы упорно игнорируете саму проблему, низводя ее до конкретных кейсов, которые вы с блеском разбиваете.
— Папку нельзя создать! —Нет, можно, с пустым скрытым файлом
— Можно создать папку и не заметить, что она не закоммитилась — Нет, я и все мои знакомые всегда смотрят на коммит!
— А если кто-то не посмотрит? — А в моей практике никому не нужна была пустая папка.
— Ну а если она все-таки нужна приложению? — Тогда создавайте ее в коде, проблема-то?

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

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

За исключением папок, знаю. Ну и опять же, дело не в том, что мне нужны папки, или кому-то нужны: я привел пример, в котором можно легко напороться на пропадание папки, даже если все делать правильно. Это не проблема папок, это проблема софта.

По ворнингу — можно попробовать поискать таску в трекере, что разработчики по этому поводу пишут, почему не добавили.

А если она нужна на этапе компиляции /публикации в каком-то инструменте, в котором я не могу вставить bash команду? А если эта папка часть структуры проекта? Менять инструменты, запилить баг репорт в инструмент, да? Пустая папка имеет столько же прав и причин на жизнь, как и пустой файл. В теме на SO на вопрос "как добавить пустую папку" тоже был ответ, что "вам не нужна пустая папка в гите". Только вот такой ответ набрал +20, а ответная реплика про бессмысленность такого ответа и про то, что папка нужна, как бы там кто не думал, набрала +300. Из этого можно сделать вывод, что таки нужна пустая папка в гите.

ВЫ не прошли ревью XD

Полагаю, должно быть !Directory.Exists() и ещё одной скобки не хватает.

UPD. Смысл этого комментария в том, чтобы показать, что даже такой элементарный код нужно:
— написать
— протестировать
— поддерживать.

А зачем это всё, если можно элементарно закоммитить пустую папку?

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

Вызов Directory.Exists вообще не нужен, т.к. при вызове Directory.Create папка просто не создастся если она существует.

Вы какие-то сгенерированные файлы в source-tree складываете? А что с ними потом делать? Все равно придётся как-то их вычистить, чтоб папка осталась пустой. Опять какой-то скрипт

Зачем папке оставаться пустой? В папке лежат файлы, но эти файлы не отслеживаются. А сама папка при этом является частью проекта. Не в любое средство разработки можно вставить дополнительный скрипт для генерации папок и проверки её наличия.

Откуда эти файлы берутся? Ну вот я новый разработчик, сделал git clone, получил пустую папку. Есть какие-то инструменты, которые а) что-то генерируют в папку и б) не умеют при этом папку создавать? Я не спорю, мне правда интересно, что это за инструментарий такой.

пустую папку можно закомитить с помощью .gitkeep, ну а в остальном полностью согласен
Да это понятно, такой костыль собственно и использую. Но тут надо понимать, что папка в таком случае все-таки не пустая, а с файлом .gitkeep. И в некоторых случаях это может помешать, когда нужна именно пустая папка.
невозможность нормально, без костылей, закомитить пустую папку
Костыль же элементарный —
touch .gitkeep
Никогда проблем с этим не испытывал…
на поддержание истории изменений

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


Рабочий код уже, как правило, есть после этой 1/4, но там бывает недостает документации, или в процессе я обнаруживаю, что можно немного порефакторить, а в итоге этот рефакторинг становится вообще слабо связан с основной целью изменений в этой ветке и его приходится отсаживать в новую. И такой процесс может продолжаться бесконечно — у меня такая рекурсия довольно часто может глубоко заходить.


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


А также отсутствие независимого версионирования различных папок.

По-моему, подмодули как раз это и есть (хотя каждый подмодуль будет отдельным проектом)

если rebase разрешен, то починить описанную ситуацию с неверным коммитом просто. Сташим свою работу, ресетим ветку на коммит назад и push --force

а как это узнать-то?) и как понять, что тебе это нужно? вот вы ведёте разработку и у вас в команде за полгода 0 ребейзов было, всё ведётся только на мерджах (реальный кейс) — и когда возникает такое, у вас не получится быстро понять, что «ну, если ребейзы разрешены, то вот это, а если нет, то вот это». И интуитивно это не понять. И знание про снимки и блобы тоже не помогут. И как вообще заподозрить, что путь восстановления ветки зависит от того, разрешён ли rebase?
И почему для push в данном случае нужен будет флаг --force? Почему раньше пушили без него? А если сейчас сделать просто push, без --force — что-то не сработает?

Потому гит и считается неинтуитивным — в нём нельзя просто что-то делать и радоваться результатам. Гит как программирование на «С» — всё здорово, пока не доходит до выделения памяти и планирования своих переменных — и тут люди сливаются и переходят на JavaScript)
Либо, если хотите, можно вспомнить vim, который тоже славится своей самобытностью. Им просто невозможно пользоваться, не изучив хоткеи и не зная, как они работают и что произойдёт.

незачем пушить работу, если не уверен

новый слоган Git) «Не уверен — не обганяй/не пушируй/не программируй, жди 100%ой ситуации»)

Бонус-трек
можно ли на основе этой картинки сказать, что гит вызывает у людей больше вопросов, чем AWS, что гит непонятнее алгоритмов?

И это мы еще до тэгов не дошли и до сквешей в фиче-бранчах...


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


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


А насчет картинки — гит сейчас везде, а алгоритмы и 50% не нужны для крудов всяких и формочек.

Ну --force — это удаление некорректного коммита. Просто так же вы его не удалите. Конечно, нужно знать, что он делает, но так в чуть других системах вы же тоже знаете какую-то особую команду для удаления неверного коммита. И точно также каждый день ей не пользуетесь.


а как это узнать-то?)

Форсированный пуш провалится, если он запрещен (не говоря о том, что в политике коммитов это должно бы быть прописано).


у вас в команде за полгода 0 ребейзов было

Как-то не верится, локально по-любому есть. Вон, я выше написал типичный сценарий моей разработки — после собственно реализации потом больше времени трачу на приведение истории в порядок, в первую очередь, чтобы другим было понятно, почему сделано так, а не иначе.


Тут вы хотели сказать, "никто никогда не пушил форсированно" (т.е. не исправлял историю). Ну так вот, даже если вы этим никогда не пользовались, найти описание флага --force и почитать, что он делает — не такая большая проблема. И само название довольно интуитивное, и в нормальных инструментах эта галочка на видном месте.


можно ли на основе этой картинки сказать, что гит вызывает у людей больше вопросов, чем AWS, что гит непонятнее алгоритмов?

Не знаю, может там большинство вопросов возникают с консольным клиентом, но никто же не заставляет им пользоваться, если у вас vim отвращение вызывает (у кого не вызывает, наверное даже рады). Я вот пользуюсь GUI клиентами.


Кроме того, git как бы сейчас в любом проекте (ну, в 90%), а прочие технологии распределены гораздо неравномернее. Естественно, то, что больше используется, больше вопросов и вызывает.

--force — это удаление некорректного коммита

пока у вас всё хорошо — вы можете месяцами вести разработку, в принципе используя 5 — 6 команд. И когда возникает впервые некорректный коммит, это выглядит как то, что пуш почему-то не работает, хотя работал. И с этого места нужно начинать разбираться. И это не выглядит как то, что нужно почитать, что делают флаги, даже будь они вполне стандартны — это нужно читать кейсы на стекОверФлоу просто чтобы узнать, что тебе эти флаги нужны. Можно полгода вести свою ветки разработки, в принципе не зная, что команды гита имеют какие-либо флаги вообще, но когда тебе это понадобится — это не будет вопросом 5 минут

Как-то не верится, локально по-любому есть

master, dev, ветки-фичи, сливания только через мердж, проекту 2ой год, бекенд на пхп7 для систем типа «Умный дом». Ребейз, кстати, и в текущем проекте не использую, не нравится количество усилий для ведения ветки

Тут вы хотели сказать, «никто никогда не пушил форсированно»

обычно у тебя просто работает pull и push, и когда они перестают работать — приходят интерны и говорят «у меня 3 стеша, у меня 4 ветки, я не могу переключиться, не могу запушить, не могу запулить, в конфликтах 30 классов, в стеше наработок 2 недели, так вышло, всегда всё было хорошо, а сегодня вот. Да, ошибся, не подумал, но сейчас чего делать-то?» (реальный диалог). Представьте любой проект, в котором есть джуны/интерны и хотя бы средняя текучка кадров в компании — и вы получите такие диалоги минимум несколько раз в месяц

если у вас vim отвращение вызывает

у меня вим не вызывает отвращение) привёл его как пример системы, в которую нельзя зайти интуитивно, нельзя просто посидеть полчаса, тыкая все хоткеи подряд и понять, как в нём работать, нужно читать хоткеи и кейсы. Вот, например, самый простой кейс — скопировать кусок текста из вима в вим. Не зная, как это делается, это не найти интуитивно.
И возьмите тот же Gimp — в нём через 5 минут можно уже рисовать и делать коллажи и мэмасики — потому что там есть инструменты, палитра, «выделить область — вставить область»
приходят интерны и говорят «у меня 3 стеша, у меня 4 ветки, я не могу переключиться, не могу запушить, не могу запулить, в конфликтах 30 классов, в стеше наработок 2 недели, так вышло, всегда всё было хорошо, а сегодня вот. Да, ошибся, не подумал, но сейчас чего делать-то?»

То есть интерны "варятся" в своём соку по 2 недели, без какого бы то ни было видимого прогресса, советов и код-ревью, а потом гит виноват?

ну вот человек получил задачу, работал над ней 2 недели, на стенд-апах всё норм, раз в неделю смотрим его ветку — ну вроде что-то делается, делал себе такой джун коммиты, пушил в свою ветку. А потом, например, узнаёт, что в ветке «develop» появились какие-то нужные изменения, не мудрствуя лукаво и ещё не зная про чери-пик, человек берёт и мерджит «девелоп» в свою ветку — чтобы получить нужный функционал. Это вполне логично и интуитивно. Пока.

Бывает, что всё норм на автомердже, а бывает, что есть кофликты, кофликты, допустим, человеку показывает ИДЕ, он их исправляет. Но если попытаться это всё запушить в свою ветку — вряд ли это закончится хорошо хотя бы потому, что его ветка не получала мердж с ветки develop. Здесь не виноват «гит» — просто со стороны это выглядит как то, что гит 2 недели работал и буквально после 2х — 3х команд мы получаем патовую ситуацию, в которой непонятно, что куда отменять и как это разрулить.
Само собой, что это не проблема git и git не виноват, просто получается, что чтобы получить нужный функционал нельзя было делать мердж ветки девелоп в свою локальную ветку. Для большинства людей это супернеинтуитивно, это как если бы перестал работать copy-paste — тебе нужен текст, ты его копируешь, вставляешь — и у тебя ломается файл с текстом и буфер обмена, и оказывается, в этот файл нельзя было вот так копировать, а надо было по-другому.
Здесь нету чьей-то вины, здесь просто ситуация такая, что выполняя, как тебе кажется, обычные и логичные действия довольно легко получить ситуацию, разрулить которую может понадобиться куча времени и нервов
раз в неделю смотрим его ветку

Ну так если у него коммиты есть в его ветке — значит, работа за эти две недели не потеряна. А исходная история звучала так, как будто всё пропало.


человек берёт и мерджит «девелоп» в свою ветку
его ветка не получала мердж с ветки develop

Вы уж там определитесь...


Мерж develop в свою ветку — это правильное решение (для "длинных" веток, разумеется), оно работает (после разрешения конфликтов, разумеется), и никак не ломает push.

А еще лучше ребейзить свою работу над develop — история сохраняется максимально линейной и понятной. Это особенно удобно, если над веткой только один человек работает, что как раз описано в случае. А если каждый коммит еще и маленький, то вообще риск, что где-то случится конфликт, с которым разбираться полдня, минимален

Проще все избавиться от неверного коммита — отменить его следующим коммитом (git revert). Тогда не нужен форсированный пуш.

А как бы вы отменили неверный коммит в другой, гораздо более понятной и логичной системе контроля версий?
Да, ошибся, не подумал, но сейчас чего делать-то?

Ну, git stash должен работать. Сташим работу, переключаемся на мастер, обновляем рабочую копию (git pull), пытаемся ребейзить свою ветку. Если ветка слишком длинная, в срединном коммите организуем промежуточную ветку, ребейзим сначала ее, затем основную уже поверх нее. Таким образом, если после половины ребейза встретитесь с хитрым конфликтом, что всю процедуру перебазирования придется прервать, чтобы снова начать с чистого листа, то вы потеряете не всю проделанную работу по перебазированию, а максимум половину.


Если git pull вернул слишком много коммитов, то можно ребейзить сначала куда-то в середину новой порции коммитов, а потом уже на master.


Конечно, все еще зависит от инструмента, я привел рабочий сценарий для TortoiseGit, как я сам делаю.


Таким образом малыми итерациями исправляете ситуацию. Если у вас привычка, что ни один коммит не ломает сборку/тесты, то процесс очень контролируемый.


В конце делаем git stash pop. Его тоже можно делать после каждого промежуточного ребейза, чтобы исправлять возможные конфликты малыми порциями, а затем снова сташить. Повторять, пока не перебазируемся на самый свежий мастер.

"незачем пушить работу, если не уверен"


А разве commit не просто у меня на диске складывает в реп. изменения? Я боюсь что завтра будет плохо моему диску, и такое бывало не раз, не хочу терять работу, пушу в бренч автоматом перед каждым перекуром. А перед пул реквестом просто все комиты делаю одним. Не помню что там за команды, мне хватает ui в vs code.

Ну в качестве бекапа в свою личную ветку(и) пушить конечно можно. И переписывать там историю хоть по сто раз на дню, если так удобно. Тут эта фраза относилась скорее к тому, что незачем пушить все в мастер, пока еще работу не закончил — а ведь именно это и случилось. Что-то запушили в мастер, в итоге пришлось судорожно все исправлять. По хорошему вообще в мастере только мерджи должны быть из других веток, уже протестированные.

То есть, быстро отменить мы не можем

Вообще-то можем, примерно так:


git push --force-with-lease origin refs/remotes/origin/master@{1}:refs/heads/master


Но у меня ни разу не возникало проблем с тем, чтобы вспомнить что именно я сделал и как это отменять


После этого ещё стоит вспомнить, что у каждого коммита есть хеши, а у ветки есть голова, которая HEAD, поведение которой…

…совершенно интуитивно.

А как же revert?

Ну от оставляет след в истории. Ещё один коммит, "обратный" исходному.
А если их несколько, то путаницы можно внести ещё больше.

git-репозиторий — это append-only граф снепшотов и пачка указателей на коммиты (их еще ветками называют). Единственная разрешенная операция с графом — добавлять коммиты (куда угодно, но только добавлять). Соответственно, все исправления — это добавление новых коммитов в разные места этого дерева и манипуляции с указателями (причем набор указателей у вас локально и в удаленном репозитории — это два независимых набора).


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

А как же дропание коммитов при рибейзе?

Так ребейз — это добавление новых коммитов к тому месту, начиная с которого ребейзитесь ;) Если я ничего не путаю, старое дерево комитов при этом не удаляется, сборщик мусора до них не доберётся ещё 30 дней. При необходимости можно сделать git reflog, увидеть хэш сталой ветки (до rebase'а), перейти в это состояние и сделать с ним что угодно (навесить указатель ветки, посмотреть историю и найти коммит, который ранее скипнули, cherry-pick'нуть этот комит в новую ветку и т.д.)
Или навесить указатель до ребейза, получив старую и новую версии, если по какой-то причине это необходимо.

Кстати, еще одно неочевидное свойство графа (я назвал его деревом в одном месте, это граф на самом деле) коммитов: он не обязан быть связным.

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


Подсказываю лайфхак, как решать 90% таких ситуаций безопасно. В любой непонятной ситуации делайте ещё один клон и экспериментируйте на нём. Это же распределённая система контроля версий, так? Значит, у репозитория может быть сколько угодно равноценных копий. Клонируйте свой рабочий репозиторий в другое место на своём компьютере, прогоняйте там какие угодно команды — хоть из головы, хоть из мануала, хоть из интернетов — и смотрите на результат. Дело пошло не туда? Не вопрос, грохнули временную репу целиком и пересоздали заново. Дело пошло в правильном направлении? Отлично, просто запускаем ту же самую команду на рабочей репе. Надо разобраться, как будут вести себя push и pull? Тоже несложно: делаем обычную копию, а рядом bare копию, и в обычной копии делаем git remote add origin <путь к bare репе>. Дело готово, можно проводить любые эксперименты без риска попортить рабочую репу.


Безопасность экспериментов на таком сетапе рождает смелость в действиях. Смелость позволяет экспериментировать больше и разнообразнее. Эксперименты дают опыт. Опыт приводит к пониманию, как работает система. Когда есть понимание, ответы на вопросы приходят сами.

Подсказываю лайфхак, как решать 90% таких ситуаций безопасно. В любой непонятной ситуации делайте ещё один клон и экспериментируйте на нём.

А как же git reflog?

reflog тоже полезен, но есть нюансы:


  1. Он позволяет разобраться с имеющимся бардаком, а эксперимент на клоне даёт шанс избежать появления сего бардака в основном рабочем проекте.


  2. Это тоже инструмент самого git, который надо дополнительно осваивать. Приём с клонами проще тем, что использует только самые базовые техники.



В общем, знать и уметь использовать reflog не повредит, но это не замена варианта с клонами, а дополнение к нему.

image

Я пользуюсь гитом через различные GUI и делаю любые сложные действия произвожу легко и непринуждённо.
В то время как коллеги мучаются с командами из консоли.


В консоли тоже периодически бывает работаю с гитом и для этого очень советую oh-my-zsh или oh-my-posh.

oh-my-god
На первых порах тоже пользовался всяким GUI, но потом перешел на консольные команды без всяких сокращений так как в GUI ощущения не те, нет полной власти и контроля над репозиторием.

Простите, а куда эта самая полная власть девается? И кто в таком случае мешает использовать GUI для просмотра текущего статуса, а консоль — для его изменения?

У меня противоположные ощущения — в гуи я вижу полноценное дерево в удобном виде.

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