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

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

И это они называют «упс»? Это обыкновенные рабочие ситуации, а не упс. Вот когда вы тщательно настроили игнор, чтобы не тащить в репозиторий лишние файлы вроде программной документации в причудливом бинарном формате, а через некоторое время откатились через git reset --hard, вот это действительно «упс». Даже «упсище».
Вот поэтому тут и «упс», а то что вы говорите — никак не ниже «Ох тыж… ё-моё!» :)
Знакомо :) Недавно решал похожую ситуацию, когда есть локальные файлы, которые не должны быть в .gitignore, но должны быть в репозитории. При этом, мне нужно периодически делать в них изменения, а git reset --hard как раз таки откатывает их к начальному состоянию. В итоге как-то удалось решить эту проблему, но это было больно…
У меня была ситуация, когда я запушил 10-мегабайтный архив в репозиторий, и после этого уже не мог его удалить, т.к. любое изменение формировало дифф в 300-400 мегабайт, который при попытки его применить на сервере падал по таймауту через полчаса.
Хм, а разве git reset --hard удаляет untracked файлы? Вот git clean -df да, это бывает упс.

4 пункт можно исправить проще:


git checkout -B future-branch
git branch -f master HEAD~

При этом нет необходимости commit'ить или stash'ить локальные изменения

Вот тоже сразу подумал о таком варианте. Особенно полезно, если репозиторий очень тяжёлый. Вариант в статье потребует двойной перетасовки рабочего каталога, что может сильно тормозить, а здесь состояние не меняется.
не знаю, я бы заменил вот этим
git checkout -b future-branch
git checkout master
git reset --hard origin/master

не нужно заново коммитить + работает если наклепал больше 1 го коммита

Вот это:


git checkout -b future-branch
git checkout master

эквивалентно этому:


git branch future-branch
либо git push --force, что чревато потерей данных при работе с веткой нескольких человек…
а как же --force-with-lease?
Да, эта опция гораздо лучше в этом случае. Но по опыту при поиске проблем с git push на том же StackOverflow ответов с --force сильно больше (справедливости ради, последнее время при этом делают пометку о возможной деструктивности такого решения). Поэтому решили лишний раз обратить внимание на то, что не следует бездумно этим пользоваться.
А так решений и советов можно предложить гораздо больше, и git pull лучше делать с --rebase для чистоты истории, и git rebase -i, и прочее, прочее. Но это уже более глубокая тема, а тут всё-таки перевод с небольшими дополнениями.

Я бросил бороться с упертыми коллегами которые предпочитают работать с rebase + push --force в feature ветках. Но есть у нас один беспардонный человек, он как слон в посудной лавке гитпушфорсит в develop, де мердж коммиты это кал а вот push --force это прелесть она так выглаживает историю коммитов. Этот Джон Сноу скоро доиграется и ему братья коллеги устроят темную.

gitlab позволяет делать защищенные ветки, в которые в том числе запрещено пушить с --force…

Я ненавижу новый хабр за то что после отправки комментария форма ввода остается на месте!

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

ну ещё из специфичного — в Visual Studio нужно сделать Save All, иначе изменения в проектых не попадут на диск.
В любом текстовом (и не текстовом) редакторе нужно сохранять изменения, чтобы они записались в файл, git же не из памяти процессов должен изменения вычитывать.

Студия по умолчанию сохраняет изменения только в непосредственно отредактированных файлах. А *.csproj — не барское это дело, пусть юзер сам озаботится.

При запуске или построении проекта она сама всё сохраняет. А просто при редактировании не сохраняет ни один редактор. Если файл проекта изменён косвенно (добавились\удалились ссылки файлы) — то это всё равно редактирование, а сохранять или нет эти изменения — пользователю решать. То что она не помечает, что файл проекта изменен, если он не открыт во вкладке — это конечно может вызвать некоторые неудобство, но это не спицифика git-a.
А просто при редактировании не сохраняет ни один редактор.

Есть контр-примеры.

Будем считать это неаккуратным округлением от «почти все».
никто и не говорит, что это специфика git, но часто именно это поведение VS становится причиной поломанных билдов. и именно из-за этого в истории git вижу докомиты по одному забытому проектному файлу.

я бы не сказал, что оно очевидно и удобно. когда ты редактируешь файл и в нем есть несохраненные изменения есть какая-то индикация этого, если изменил проектный файл ничего нет. добавил файл в проект нажал Ctrl-S, вроде бы должно хватать, а нет.
Просто фокус у Вас на этом новом файле — она его и сохраняет. Выбрать проект в обозревателе проектов и нажать и нажать тот же Ctrl-S, и всё работает. Что нет индикации, что этот файл изменен — это минус, да я согласен. Ну а забывчивые, или кого это раздражает могут переназначить на Ctrl-S команду «Save Aal».
В Rider это происходит автоматически.
НЛО прилетело и опубликовало эту надпись здесь
Как часто бывает — до конца дня не успел закончить задачу. Остается либо не коммитить ничего и попасть если не окажется доступа к файлам, либо коммитить, но часть [неработающего] кода.

Что с этим делать?
Или git-stash, чтобы сохранить изменения локально, или комитить и пушить во временную ветку.
Коммитнуть в отдельную ветку, которую можно и пушнуть в remote, чтобы данные точно не потерялись. В коммите при этом можно указать, что он WIP. И вообще в такую свою dev-ветку можно коммитить хоть каждые 10 минут и с не очень информативными комментариями. А когда всё готово — чистить историю с помощью git rebase -i. Ну или в случае с Gitlab, он умеет делать squash коммитов MR при мёрже.
А если работаете с bitbucket, то можно сделать форк и плодить ветки в неограниченных количествах. GitLab тоже позволяет делать форк, но у него какие-то проблемы с автоматической синхронизацией — то работает, то отваливается.
Когда работал в компании где был развернут bitbucket server, то пользовался форками с удовольствием.

Про удаление remote — уже довольно долго можно делать git push origin --delete branch, что куда понятнее.

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

Вот так

(тут мне следовало бы вставить пометку «я пиарюсь»)

Ой-ой. Начиная с пункта 4 — вещи, которые не следует делать, пока как следует не вкуришь git (а тогда эта статья уже не нужна).
Я после своего первого git reset, наверное, полдня потратил на изучение git только для того, чтобы больше так не делать (пропала история… удалось восстановить) и полюбил mercurial, в котором на такие грабли не наступал ("святость истории" — хотя, конечно, и там есть послабления).

Если же вы всё-таки коммитнули изменение, потребуется дополнительный предварительный шаг:


А если вы сделали уже несколько коммитов и тут заметили, что лишний файл там лежит? :)
Я в этом случае нашёл рецепт такой:

Удаление файла TeamControlClient.sdf из всех коммитов репозитория в e:\TeamControlClient.
Сначала удалим файл:
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch TeamControlClient.sdf' \
--prune-empty --tag-name-filter cat — --all

Теперь создалась новая «ветка» с места появления файла.
А теперь клонируем репозиторий с этой веткой, не заходя в старую.
git clone file://e:/TeamControlClient e:/TeamControlClientNew


В новом репозитории файл будет физически удалён.

Уж не знаю, насколько это правильно. Но помогло.
Способ нормальный, разве что никакой git clone там не нужен. Он правилен ровно настолько же, насколько любые другие манипуляции, приводящие к потере id коммитов, ребейз, например. На ветках, доступных другим разработчикам, использовать по возможности не стоит.

Минус filter-branch — gpg-подписи теряются (rebase же пробует переподписать новые коммиты, например).
Без клонирования размер репозитория не изменится после удаления файла (файл как бы физически остаётся). Поэтому, как я понимаю, и делается клонирование.
Тогда понятно, но достаточно выполнить git gc --prune=now, если хочется удалить сразу (без этого garbage collector удалит его только через две недели).

Слишком сложно, кмк. Можно сделать коммит с удалением файла, потом через интерактивный rebase передвинуть этот коммит ниже до нужного и объединить, можно через него же с отметкой нужного коммита "edit", можно вручную сделать ветку от нужного коммита, в ней commit --amend с исправлениями, потом через cherry-pick добавить все следующие коммиты.

А это физически исключит файл? У меня удаляемый файл привёл к увеличению объёма на 50 Мб. Просто убрать файл из истории не помогало никак.

Есть команды git gc и git prune, но я ими не пользовался, потому про нюансы не в курсе.

Я для удаления файлов и sensitive info из истории использовал BFG Repo-Cleaner – неплохая штука.

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


На --amend галочка в диалоге коммита, на файл галочка в списке файлов, причем git add для untracked автоматически выполняется, на отмену изменений пункт меню "Revert", на переименование ветки F2 в списке веток, на удаление пункт меню там же, и для удаленной тоже, reset на коммит пункт меню в списке коммитов, "Show reflog" вообще рядом с "Show log", и иногда случайно на него тыкаешь.
Коммит не в ту ветку требует аналогичных действий, нестандартный checkout или reset надо конечно в консоли делать.


PS: "Опять кто-то влез с GUI", но статья для неопытных пользователей, и кому-то это поможет сохранить время и нервы)

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

Чтобы git pull не делал merge — используйте git pull --rebase.

Это наверное самая полезная фича при работе в команде.
Я бы даже посоветовал сделать
git config --global pull.rebase true

Но вообще это странная сиуация. Как в ветке может оказаться по разные стороны (локально и в ремоуте) разные коммиты? Что произошло?

Два человека работают с одной веткой, у обоих одинаковый набор изменений, первый делает коммит в мастер и делает пуш, следом второй делает в ту же ветку коммит, и при попытке запушить он получит режект. Вот git pull --rebase позволяет сделать историю коммитов линейной, без merge-коммитов.

А зачем они работают с одной веткой? Какая в этом нужда?

Trunk-based development, когда не надо делать 100500 веток, чтобы потом 100500 раз не мержить и не нарываться на конфликты в разных местах.

Всмысле 100500 раз мерджить? Во-первых не мерджить, а ребейзить.
А во-вторых, количество ребейзов только возрастает. И, кроме этого, теперь надо ребейзиться не только с основной ветки разработки, но и со своей собственной. Количество работы и конфликтов возрастает в разы просто.

Не хочу обидеть, но мне кажется, что вы не до конца понимаете концепцию Git и продолжаете ее использовать, как Subversion.

Вы видимо меня не поняли) Trunk тут не про Subversion. Вот есть ветка master. И все разработчики на проекте пользуются исключительно ей, совмещая свои пуши с ребейзом, чтобы с одной стороны история коммитов была линейной, а не засоренной коммитами типа merge branch with..., а с другой стороны для того, чтобы не плодить кучу отдельных веток от master. За которыми потом еще следить надо, а нужны ли они до сих пор, или их можно уже давно удалить.


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


Релизы можно делать и по тегам, это вполне вписывается в концепцию) Более того, концепция Git не заключается в одном лишь ветвлении.

потом, когда приходило время свои наработки вливать обратно, начинались танцы

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

Сценарий_1:
Есть мастер. Отпочковались 5 веток — каждый делает свое дело. Каждый, по окончании работы, заребейзился перед прогонкой тестов.
Итог: 5 ребейзов от мастера.

Сценарий_2:
Есть мастер. Отпочковались 2 ветки. Над одной работает 2-е парней, над второй 3-е. Все закончили работу. Каждый делает ребейз со своей ветки в ремоуте. Итого у нас 5 ребейзов. Пока все ок. Дальше первые двое парней отправляют свою поделку в мастер. Опа — у трех остальных несхождение с мастером — нужно ребейзиться перед тестами.
Итог: 5 ребейзов с ремоута и 1 с мастера.

Эта дельта в 17% будет сохраняться при любой активности и объемах работы. Но чем больше у вас будет людей и веток, тем больше эта дельта в 17% будет весить в абсолютных цифрах ресурсов.

В сухом итоге получается, что вы сказали примерно следующее:
Мы не любим решать конфликты при мерджах/ребейзах, поэтому мы увеличиваем количество этих конфликтов на 17%.


И это:
За которыми потом еще следить надо, а нужны ли они до сих пор, или их можно уже давно удалить.

Бренчи должны автоматически удаляться после мерджа. Это фича-бренч. Фича сделана и протестирована? Видимо да, раз отослали PR к мастеру. Если она сделана, зачем бренчу жить? Будет баг — будет другой бренч на баг.

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

Вот я изначально имел ввиду второй сценарий. Либо пара разрабов сидит в мастере и пилит что-то, либо та же пара разрабов делает для себя фиче-ветку, и пилит в ней, и чтобы не было в рамках этой ветки мерж-коммитов и юзается git pull --rebase.


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

В дополнение ко всему, у нас разработчики используют один центральный репозиторий, поэтому практика pull-requests у нас не пользуется популярностью, где допустимо пилить фичу например месяц, и периодически ребейзить свою ветку в своем репозитории с force-push без опаски сломать все основное. У нас же в "долгие" фичи-бранчи приходится подмерживать мастер, и в итоге мы имеем кучу мерж-коммитов в рамках одного репозитория, что в общем-то не очень приятно наблюдать при просмотре истории.
Открыл сейчас наш upsource, тыкнул в лог коммитов, у одного мерж-коммит из-за того, что он пилил на разных железках код в эту ветку, и сам с собой словил рассинхрон и сделал просто git pull, второй мерж-коммит синхронизация с мастером, следом третий мерж-коммит для синхронизации с мастером уже другой ветки от другого человека… Это мелочи, но напрягает.

У нас же в "долгие" фичи-бранчи приходится подмерживать мастер, и в итоге мы имеем кучу мерж-коммитов в рамках одного репозитория, что в общем-то не очень приятно наблюдать при просмотре истории.

Просто перед слиянием в мастер делается rebase+squash, в результате история получается приятная глазу и вполне укладывается в идеологию "одна задача – один коммит".


практика pull-requests у нас не пользуется популярностью, где допустимо пилить фичу например месяц

Как связан размер фичи с пулл реквестом? В чем проблема сделать ветку + пулл реквест пусть даже для однострочного коммита? Лишних действий почти нет, зато наглядно видно, что именно сейчас уйдет в мастер.


А пушить напрямую в мастер-ветку – это как-то отчаянно, на мой взгляд – это какие-то админы первого типа.

А пушить напрямую в мастер-ветку – это как-то отчаянно, на мой взгляд – это какие-то админы первого типа.

В этом нет ничего страшного, если релизы делаются по тегам. Так или иначе код перед релизом ревьювится.

А смысл в таком ревью?


  1. Кода много – явно больше одного таска => нормально отревьюить его будет на порядок сложнее.
  2. Код уже в мастере, т.е. на него потенциально завязан другой код, исправить его сложнее, и во многих случаях на это просто забьют.

Такой ревью будет больше для самообмана. Или для очковтирательства менеджеру.

Кода много – явно больше одного таска => нормально отревьюить его будет на порядок сложнее.

Задачи в коммитах можно помечать маркерами типа [TASK-999]. Тогда проблем найти нужное не будет.


Такой ревью будет больше для самообмана. Или для очковтирательства менеджеру.

А при чем тут менеджер, если код ревьювится программистами для улучшения или исправления кода другими программистами?

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

Просто перед слиянием в мастер делается rebase+squash

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


Как связан размер фичи с пулл реквестом? В чем проблема сделать ветку + пулл реквест пусть даже для однострочного коммита? Лишних действий почти нет, зато наглядно видно, что именно сейчас уйдет в мастер.

Цитата вырвана из контекста у вас. там было перед ней:


… у нас разработчики используют один центральный репозиторий, поэтому практика pull-requests ...
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.