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

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

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


Какая-то странная у вас аргументация.
Ни разу не сталкивался с ошибками git клиента
А вот то что в VS 2019 появился squash, мне во многом облегчило жизнь
Ну строго говоря Вы подтвердили тезис — squash в консольном клиенте был давно :)
Но я не навязываю кому-то инструмент, вполне возможно у Вас другие сценарии использования.
Ни разу не сталкивался с ошибками git клиента

Ooo, вот только что нарвался. В Windows 10 на одной машине вдруг стала сбоить команда git subtree add. Три дня на ушах стоял. Потом обнаружил, что глюки имеют место только при запуске из-под Far'а. Не только напрямую, но даже если из него Pycharm запустить. В конце концов выяснилось, что по непонятной причине Far запускается в режиме совместимости с Windows 8.1.

Не используйте команду pull

Категорически поддерживаю. Эта команда делает две совершенно разные вещи: fetch + merge, причем с непредсказуемым результатом, not the unix way. Очень жаль, что она есть, без нее было бы меньше магии и меньше путаницы.


Советую вместо git pull делать примерно так:


git fetch
git status -sb

И после этого уже merge или rebase.

Есть потрясающая опция git config --global pull.ff only — пулл делает только fast-forward и фейлится, если он этого не может сделать. Это избавляет от необходимости контролировать каждый fetch+merge и при этом защищает от пулла не в ту ветку.

Как многие жалуются опций у git'а действительно много, но смысл совета как раз в том чтобы не делать два разных действия одновременно. fetch — для синхронизации базы merge/rebase для апдейта ветки и рабочей копии проекта.

Ну и `ff-only` будет фейлиться если над проектом работают много разработчиков и основная ветвь двигается часто.
ff-only будет фейлиться

В этом-то и задумка. А ещё если локальные изменения не пересекаются с входящими (затронуты разные файлы, и в вашей ветке сверху нет незапушенных коммитов), то по идее такой pull должен прокатывать как fast-forward.


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


А про fetch, кстати, многие забывают или не знают про такую классную опцию как --prune — вместе с выкачиванием новых веток/коммитов, она удаляет те, которые были удалены на сервере (имеются в виду remote-ветки. Ваши локальные чекауты останутся нетронутыми).

Всё так! Спасибо за комментарий!

Опция pull.ff only очень хороша, но не все про нее знают, и мало у кого она установлена. В моем конфиге она есть, но я в принципе не делаю git pull. Для таких случаев у меня есть алиас git fff, что значит fetch fast-forward, вот кусочек моего конфига:


[alias]
s   = !git status -sb
f   = !git fetch origin --tags --prune && git s
ff  = !git merge @{u} --ff-only && git l1 && git s
fff = !git f && git ff
Крутой конфиг, спасибо!
Я правда не сторонник заводить на всё алиасы, потому как прыгал часто на чужие терминалы, но как пример — очень классно :)

Можно разу делать pull --rebase

Можно, если знаешь что делает команда, то можно делать сразу два действия с выбором режима. Но я намного реже хочу делать ребейз сразу с фетчем :)
svn мне нравился тем, что многое он не умел, но при этом был на порядок проще. Все эти ff, wip, squash (и update-index --assume-unchanged который ниже — тут наверняка почти никто не знает) — мне неизвестны, хотя с гитом лет 5 работаю. Ну как неизвестны. wip — впервые вижу, только stash знаю, а остальные — слышал, но ни разу не применял.
Так что я бы добавил «изучи инструмент полностью». Проблема в том, что многие возможности это «о, прикольно» — но применяться не будет почти никогда. Но… представим что я программист. Что от меня требуется? Написать код, протестировать, и закоммитить. А потом вылить на удаленный сервер для тестов сервером, другими людьми. При этом голова чем занята? Почему этот баг не воспроизводится в отладчике, как сделать новую фишку максимально быстро и красиво и проверить все граничные случаи… А не о том, что я должен делать fetch (который бывает разный, смотря какие опции), потом merge… и подобное.
Это так, мысли вслух.
git проще чем может показаться. И в то же время это очень мощный инструмент, заслуживающий чтобы ему уделили время на изучение. В документации есть не только про то как работают команды, но и про то как работает сам git. И поняв внутреннею механику, которая повторюсь довольно проста(как и многие гениальные вещи), вам будет намного понятнее как применять команды. Рекомендую довольно подробную статью здесь на habr ну и глава в git-scm

"wip" — аббревиатура от "work in progress", используется как плейсхолдер и на гит никак не завязано. Да, это я как-то погорячился, упомянув его без расшифровки.

«переключиться на основную не вливаясь» — для этого есть stash, а как закончили то stash pop. Иначе получается незаконченная каша. Лучше тогда «работа в ветке, сделал мелкий блок для конкретной задачи — закоммитил», тогда и stash не нужен. А иначе это «мусорный» коммит с нерабочим кодом. Впрочем, если потом идёт слияние как 1 итоговый коммит с потерей промежуточных, не помню опцию, то тоже имеет место быть.
В общем, гит может много. Это его и сила и слабость, большинство останавливаются на clone-pull-commit-push.
ЗЫ Про stash уже увидел чуть ниже, отвечал по мере чтения, простите.
Оставить там коммент — не смогу, ответ раз в час.

Только влитое временно в [временную, ежели нужно] ветку там в ветке и останется до потребности/сноса.
Да и в кашу stash превращается ещё легче.

Да у svn были свои плюсы, но не все настройки и параметры git'а нужно помнить или знать. Я как раз старался описать ровно то чем пользуюсь буквально каждый день, а не эпизодически.

… я программист. Что от меня требуется? Написать код…


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

git stash делает фейспалм

А вот не всё так однозначно.


Есть несколько моментов. Если пытаться работать одновременно с несколькими ветками и делать stash в нескольких, то после переключения на исходную ветку сильно задумываешься, что нужно применить вместо git stash pop. Если стешить совсем небольшие изменения, то можно вообще не вспомнить, что делал стеш, и потом сидишь и удивляешься, почему результат не тот. А окончательно я решил для себя вопрос, когда как-то раз stash сохранил изменения только в модифицированных файлах, а те которые были untracked — безвовратно протерялись (про то, что их надо было отдельно включать через --include-untracked я тогда не знал). Так что текущие изменения в ветке, которые в любом случае в неё попадут, проще сразу коммитить, пусть даже во временный коммит.


А вот stash удобно пользоваться для хранения хаков, которые можно применить к любой ветке и потом не жалко откатить перед "настоящим" коммитом.

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

Вот-вот, поддерживаю.

Я помечаю такие временные бекап-коммиты крестиком, примерно так:


[x] Temp

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


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

можно придумать по аналогии с !fixup сообщением, и настроить хук
так и избежать «плохих» комитов и автоматизировать свой flow

Тоже не понял, зачем так делать. У меня есть только одно объяснение… Я сам так делал, когда не знал про git stash )
UPD: Увидел новые сообщения. Причина использования временного коммита понятна, но всё же на вкус и цвет.

Отвечу более развернуто.


Фундаментальное преимущество git — его распределенность: вся история есть локально, и можно полноценно работать даже когда нет сети, поэтому он такой быстрый. Сеть нужна всего в двух случаях: скачать новые коммиты с удаленного сервера (git fetch) [причем для всех веток сразу], отправить свои коммиты на удаленный сервер (git push).


С этой точки зрения команда git pull (с какими угодно опциями) — это "супер-команда", которая скачивает новые коммиты [также для всех веток сразу] и сразу же пытается автоматически интегрировать их с локальными коммитами на текущей ветке.


Для меня это две совершенно разные задачи. Отказ от этой "супер-команды" дает следующие преимущества:


  1. Логическое разделение двух несвязанных операций.
  2. Возможность увидеть и сравнить все изменения до их интеграции.
  3. Возможность самому решать как делать интеграцию (--no-ff, --ff-only, standard rebase, rebase-via-merge, --squash).
  4. Не запускать лишний раз скачивание коммитов с сервера. Именно поэтому git pull — всегда такая медленная операция, тогда как сами по себе merge и rebase быстрые, потому что им не нужна сеть.
  5. Есть ясное понимание того, что происходит, без каких-либо сюрпризов. Не надо полагаться на какие-то опции этой "супер-команды".

Странно, что совсем не упомянуты хуки. Инструмент, которым я пользуюсь каждый день — привычка называть ветки единообразно, типа "my_feature_PRJ-001", где PRJ-001 — номер задачи в жире/редмайне, плюс миниатюрный скрипт commit-msg, который номер задачи подставляет в начало сообщения коммита. Если есть интеграция с ишью трекером, очень удобно смотреть что делают другие и вспоминать, а что делал сам.


О, о, а ещё есть такая суперская вещь как git update-index --assume-unchanged <path to file> — изменённый файл перестаёт отображаться как изменённый и соответственно нигде в гите не всплывает до тех пор, пока не приходят коммиты с этим же файлом или вы не решаете отредактировать его ещё раз. Очень помогает, если в вашем репозитории есть отслеживаемые конфиги, а вы у себя хотите держать их альтерированную версию и не хотите, чтобы они мозолили вам глаза.


Я обожаю гит и его возможности для упрощения себе жизни, простите. >_<

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

А вот интересно было бы послушать, на самом деле. Про "базовые" лайфхаки много пишут, а про продвинутые меньше. У гита есть ещё порох много заначек; про тот же git commit --fixup я например только из этой статьи узнал.


Вам спасибо за поднятие интересной и полезной темы.)

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

Какой-то крайне сомнительный совет :) Во-первых, не дофига ли чести для рутинного действия, которое почти всегда fast forward, как уже выше отметили для тех, когда это не так, есть опция в конфиге. Во-вторых,


git pull -r

Сделает то ж самое, что приведено в примере, только автоматически


Правильный совет — разберитесь уже как работает git pull и как решать проблемы, которые он создаёт)

Fast forward при распространённых workflow почти никогда не случается — 90% случаев заканчиваются лишним merge'ем, совет как раз в том что стоит контролировать два разных действия, если человек умеет пользоваться pull'ом, то этот совет ему уже не нужен :)
> git pull -r

У меня даже так: pura = pull --rebase --autostash

Но тут всё-таки надо понимать, когда нужен pull с мержем — иногда бывает.

Имеет смысл упомянуть tig, определённые вещи там делаются невероятно удобно.


git clone git@github.com:gurugray/gurugray

К пункту 6 можно добавить настройку в .gitconfig:


[url "git@github.com:"]
  insteadOf = https://github.com/
tig прикольный, у меня он всегда по умолчанию установлен, но как-то не зашёл на каждый день

Про isteadOf я писал когда-то заметку, настройка удобна, особенно когда работаешь с разными ремоутами
Надо бы ещё совет «Пользуйтесь ключом -p».

И горячо поддерживаю, то что нужно работать из консоли, т.к. есть люди, кто познакомился с git используя ide'шки, для них мысль об обычном merge панику вызывает. Т.е. консоль это то, что надо уметь, GUI лучше осваивать, когда освоил CLI.
Надо бы ещё совет «Пользуйтесь ключом -p».

Это который у git add ?


Штука полезная, но я ей не пользуюсь каждый день, может потому что люблю фиксировать комиты часто, а потом перебазировать так как мне больше нравится семантически :)

У `git add`, `git checkout` у `git reset` и может ещё где, сходу не соображу. Мы ввели правило, все изменения в гит добавлять с таким ключом, сразу пропали вопросы, вида «а это тут зачем?». Т.к. бывало человек коммитил в одну ветку, то что должно быть в другой, либо нажал что-то в редакторе, современный редактор рассчитанный на использование систем контроля версий это дело сохранил, а дальше получаем на ревью PR в котором есть нерелевантный код или вообще мусор.

Такой подход требует большей дисциплины, чем я могу расчитывать :)
Но в нём определённо есть плюсы!

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

Я придерживаюсь практики — больше комитов, потом агрегация в атомарные и стабильные состояния с правильным описанием.


Дисциплину я понимаю так — думать о том что и почему ты комитишь, хотя бы так :)

Ваш подход, IMHO, может работать как индивидуальная практика. В команде такой применять вряд ли получится. Т.к. уж больно замороченно, потом для создания агрегированных состояний есть Pull request'ы с ревью, мы у себя в команде их сквошим при мерже. Т.к. по факту, на мне важно один там коммит в истории или сотня.

Ух, вот в командной практике я стараюсь отговаривать сковшить PR, мне кажется минусы от этой практики превышают её плюсы


И тогда не очень понятно зачем заморачиваться с add -p и подобным если потом всё в одну кучу схлопываете?

-p позволяет коммитить только то, что нужно закоммитить. А PR под фичу. В истории потом понятно, кто когда и зачем реализовывал. Неудобно только, что связь с dev ветками теряется, но по факту она редко нужна.

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

Флоу типичен в таких ситуациях: есть N+M изменений, из которых N идут в планируемый коммит, а M — это то, что не должно коммититься, потому что из них 90% это log(«TMP: точка 25») или что-то подобное, часть — изменения опций для такого дебага, и т.д. — и они перекрываются по файлам.

Более того, я обычно использую даже не add -i или -p, а add -e — с редактированием диффа. У него есть некоторые маргинальные ограничения (например, добавление нового файла не проходит по частям), но в >99.9% случаев помогает разделить две категории изменений, даже если они перекрылись в одной строке.

у меня такой подход не очень прижился, мне мешали переключения между ветками (теряется то что не закомичено), мешали отрыв от контекста — то что не закомичено, не существует :)


потому мой флоу скорее — атомарно комитить дебаг, чаще ребейзиться и в конце выбрасывать дебаг из истории комитов

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

есть ещё worktree, но мне на каждый день не особо прижилось
но на маленьких проектах может даже быть очень эффективным

для них мысль об обычном merge панику вызывает

Это скорее от неумения работать, чем от GUI

И потом, до сих пор не понимаю, как можно говорить про «Обзорность» и консоль в одном предложении. Пытаться построить псевдо-дерево в консоли, чтобы сымитировать возможности GUI.
¯ \ _ (ツ) _ / ¯

пример с деревом в консоли — мне правде удобнее ¯ \ (ツ) / ¯
и возможность сразу грепать по ней и делать первичный поиск для меня тоже важна

Ну, люди умеющие консоль и боящиеся мержа может и встречаются, но мне не попадались. Да ребейз не каждый трогал, но, на мой вкус, в норме, он и не нужен.
Еще пара слов в копилку про pull:
Его так часто используют с опциией --rebase, что в самом git можно переопределить поведение команды pull:
git config --global branch.autosetuprebase always
После выполнения этой команды все команды git pull будут интегрироваться с помощью команды git rebase, а не git merge.
Признаюсь, сам только что об этом узнал, когда полез смотреть что за ключ -r у команды pull

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

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

Это разве что если вы ведете свои проекты на github. Многие же кампании поднимают свой enterprise gitlab и магической публикации активности в гитхаб не происходит.

это так, да :)

Как то странно git pull попал в немилость, а git commit --all в рекомендации)

А уж ничего разрушительнее commit --all --amend --no-edit в алиасах я представить не могу

Тут я согласен, что commit --all может быть зачастую вреден, но я не рекомендовал его специально, возможно стоило и тут несколько предохраниться :)

git config --global core.editor "code --wait" а для чего это нужно?

это нужно для того чтобы редактировать сообщения комита в редакторе VSCode в частности
core.editor может быть любым удобным редактором

Зарегистрируйтесь на Хабре, чтобы оставить комментарий