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

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

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

Я буду обновлять страницу перед отправкой комментария.


https://habr.com/company/flant/blog/419733/#comment_18981183

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

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

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

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

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

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

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

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

Или использовать встроенный git-интерфейс для коммитов, там изменения "в памяти" учитываются

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

Что с этим делать?
Или 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

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