Pull to refresh

Comments 94

Консольной команды не знаю, но в Git Extensions есть функция для поиска и просмотра всех потерянных коммитов. Как правило, достаточно отсортировать их по дате — и нужные сразу же видны.

PS а для того, чтобы потерять коммит, достаточно сделать checkout ветки origin/master — при этом ветка master сразу же «прыгнет» на тот же самый коммит — а коммиты, которые были в master, но не были в origin/master — потеряются.
Вот только потом можно сделать git checkout обратно в мастер и они волшебным образом вернутся на место.
Нельзя. Старая ветка master исчезла.
Поясняю: речь идет о команде git checkout -B master --track origin/master
Если вы спросите, кто такое делает — отвечу: git extensions именно эту команду и выполняет при попытке «просто забрать» ветвь origin/master.
Что значит «просто забрать»?
Согласитесь, этот пункт меню выглядит безобидно?
image
Это — удобные сокращения к гиту. Работают отлично — пока понимаешь, что они на самом деле делают.
Увы, снова обманули.
Вот удобные сокращения к гиту:
alias s='git status'
alias k='gitk --all'
alias gl='git log --graph --decorate --pretty=oneline --abbrev-commit --color -n30'
alias gu='git remote -v update'
А мне кажется, обманули все-таки вас.
PS зачем вы вообще спорите? Вы что, отказываете другим людям в праве иметь любимый инструмент, отличный от вашего?
Как это меня обманули? Что, все-таки есть команда «просто забрать»?
git "просто забери" origin/master
Есть хак для «просто забери»: прописываете в настройках удалённого репозитория, что хотите всё забирать себе и прописываете alias fu = pull --ff-only upstream:
[remote "upstream"]
        fetch = +refs/heads/*:refs/remotes/upstream/*
        fetch = refs/heads/*:refs/heads/*
        url = git://github.com/user/repo
        pushurl = ssh://git@github.com/user/repo
        push = refs/heads/develop

Последняя строка для того, чтобы не push’ить по‐умолчанию что попало, вторая для того, чтобы иметь «резервную копию» всех ссылок. Вся соль в третьей: она говорит git «по‐умолчанию забирай все ветки».

Но поведение mercurial всё же более понятно и более удобно.

PS: Если вы под «просто забери» не имели ввиду «просто забери всё», то дело решается либо так же (но с заменой звёздочек на имена веток), либо явным git fetch origin master:master или git pull --ff-only origin master.

PPS: Сразу говорю, что git fu с моим alias’ом и моим workflow будет постоянно считать, что провалился. Просто git fetch будет проваливаться с сообщением, что не может делать fetch в текущую ветку, поэтому используется «pull в значении fetch, если текущей ветки нет в upstream, и pull в значении „сделай всё и подвинь текущую ветку“, если она есть». Но при этом после fetch будет проваливаться merge, т.к. он не знает, с чем сливать (что меня устраивает, т.к. я хотел сделать только fetch).
Из-за этих клиентов одни проблемы
git reflog бывает полезен в таких случаях
Не знал про него. я так понимаю, что он выводит тоже самое что и содержимое logs/refs/heads/branch?

А насколько нормально такие вещи, как потеря коммитов?
Да, это более «user-friendly» интерфейс.

Потеря коммитов в моей практике не встречалась, а вот повреждение репы — это очень печально. Это когда содержимое не соответствует хешу и git fsck говорит «бида-бида». Это почти нельзя восстановить без бекапа.
В догонку к хорошим практикам. Всягда, перед тем как вообще делать коммит, а уж тем более push, нужно всегда выполнить:

git fetch origin
git status

Каждый кто серьезно пользуется гитом просто обязан это делать.

Всягда, перед тем как вообще делать коммит [...], нужно всегда выполнить:
git fetch origin

Это немного противоречит букве D в аббревиатуре DVCS.
Почему же? Если есть апстрим (а мы про это говорим) то всегда есть риск что ветки могут разойтись.
Чем раньше это словим тем лучьше.
4 часа работы потерял из-за этого бага, при выполнении pull и rebase у меня оказался заблокирован на запись один файл, git при rebase не смог его ни модифицировать ни удалить, выдал ошибку и удалил мой последний коммит со всеми изменениями. Работал через SmartGit, но он вызывает консольный git при этом.
именно удалил или перескочил как у меня?
Удалил последний локальный коммит вернув все изменённые файлы к состоянию предыдущего коммита, который был синхронизирован с репозиторием
А вы пробовали его поискать, как я пишу или как ниже советуют через git reflog?
К сожалению нет, не пробовал.
В дальнейшем эта ситуация повторялась, с менее плачевным результатам и тоже один файл у меня был заблокирован на запись извне при попытке сделать rebase.
Ну тут мало чего сделать можно: git совершенно не рассчитан на такие чудеса, как заблокированный файл, который нельзя удалить или, там, прочитать. В Linux'е невозможность удалить файл при наличии прав обозначает, что произошла какая-то катастрофа (скажем файловая система рассыпалась или баг в ядре случился) и его обработка в git'е под это заточена.
UFO just landed and posted this here
Вполне себе кросс-платформенный. Везде где нормально поддерживается POSIX он работает отлично. Ну а если Microsoft решил смухлевать и добавить ровно столько поддержки, чтобы пропихнуть через правительственные контракты, но не столько, чтобы программы можно было реально использовать — то кто ж вам виноват? Не перекладывайте проблемы с больной головы на здоровую.
Да ну? Делаете chattr +i filename или chattr +a filename от root’а и получаете неудаляемый (даже самим root’ом*) файл при наличии прав на него. Также можно при желании настроить AppArmor/SElinux, чтобы git не мог что‐то удалить (но при этом права, в т.ч. на запись будут). Не знаю, правда, что вернёт stat в последнем случае. Но в обоих случаях бага в ядре и рассыпавшейся файловой системы нет, а невозможность удалить файл есть.

* root всё же может удалить, если воспользуется chattr (а также прямым редактированием блочного устройства, монтированием файловой системы с использованием собственного драйвера, игнорирующего атрибуты, или любым другим из 100500 грязных хаков, недоступных для обычного пользователя).
UFO just landed and posted this here
Делаете chattr +i filename или chattr +a filename от root’а и получаете неудаляемый (даже самим root’ом*) файл при наличии прав на него.
Вы сами-то поняли что сказали? Как вы можете говорить, что у вас есть права доступа к файлу, если вы их сами же и отобрали?

Да, в Linux'е есть больше одного способа отнять права доступа к файлу. Но это ничего не меняет по сути: либо права у вас есть и файл можно удалить, либо их нет — и тогда вы ССЗБ если сделали так, что к части исходников у вас доступ есть, а к части — нет.
Вы сами-то поняли что сказали? Как вы можете говорить, что у вас есть права доступа к файлу, если вы их сами же и отобрали?
Права на запись и атрибуты файла — это разные сущности. Этим действием я ни у кого не отбираю права, я оставляю указание драйверу файловой системы, что ему надо делать с этим файлом. Поэтому root спокойно игнорирует отсутствие у него прав на запись, но не игнорирует атрибуты. Правда, несмотря на различный смысл, проверяется всё (атрибуты и права) практически в одном месте, по крайней мере в ядре linux.
А можно уточнить какой версией Git вы пользуетесь?
Интересно узнать это у всех версий такой глюк или допустим в 2.1.+ исправили.
Мы используем git flow и ребейзим feature-ветки на develop постоянно. И только их пушаем.

git pull — злое злостное зло.

Лучше работать в тематических ветках. Git тем и хорош, что ветки в нём — не то, что trunk в svn'е.

ps: Повторяющиеся конфликты решает rerere.
git pull — злое злостное зло

Не согласен с вами. Git был придуман для максимально удобного и правильного merge, без какой-либо магии и костылей. Поэтому не нужно бояться git pull. В конце концов, всегда можно все отменить и откатиться назад.

Насчет тематических веток и Git Flow — штука интересная, но как по мне, Git Flow — это просто здравый смысл в использовании Git-веток. Так что зная базовые принципы веток Git и имея здравый смысл, вполне можно обойтись без Git Flow.
Кто хочет сохранить прямую историю коммитов делает git pull --rebase && git push.
rebase в свою очередь видимо что-то потерял, из-за чего автору пришлось искать ответ на довольно непростой с ходу вопрос.
Я пользуюсь SmartGit там под действием pull подразумевается сразу же и rebase или merge, если не удается rebase.
Тогда, может, это не проблемы гита, а клиента (SmartGit в данном случае)?
Не думаю, он выполняет консольные команды. Так что rebase Git сделал в итоге
Не понимаю посыл. Ну сделает он фетч и что дальше? Ведь все равно придется мержить или ребейзить
git rebase — потенциальная проблема. Например, некоторые проблемы rebase описаны в книге «Pro Git» — "The Perils of Rebasing". Моё мнение — лучше делать merge, чем rebase.
UFO just landed and posted this here
И иметь отвратительную историю, где непонятный комок веток? Нет, спасибо.

А уж не ребейзить пушнутые комиты — это очевидно. Ребейз только тематических веток д.б., и точно не develop/master.
Не буду спорить, все очевидно. Но это очевидно для вас — человека, который знает разницу. А многие, я полагаю, просто делают rebase, не думая о последствиях. Отсюда и проблемы, вроде описанной в данной статье.

Что же насчет непонятных коммитов в истории — во-первых, а что в них непонятного? Они же помечены, как merge commit, и в них есть полезная инфа. Во-вторых, GitHub / Bitbucket специально «приглушают» их в истории коммитов, так что у меня они не вызывают там дискомфорта.
Понятно: начало ветки, коммит1, коммит2, коммит3, merge blabla

Не понятно: начало ветки в далёком июле, коммит1,… over100500 коммитов..., merge from develop, коммит2, over100500 коммитов..., коммит3, merge blabla

Когда все коммиты аккуратно сложены один над другим, сразу видно, что делал человек, почему он это делал и вообще за что ему зарплату платят. Когда коммиты раскиданы, как носки по комнате, в коде мясо с фаршем и вообще всё тлен, то…

Короче я — сторонник секты аккуратности и знаю, как работает мой инструмент. И совет даю всем — пойми, что ты делаешь и думай о других, кто будет читать твой код. Это заповедь, скрижаль.
UFO just landed and posted this here
Статья про то, как в этой ситуации коммит найти. Это полезная информация, которую, как оказалось, не все знали.
UFO just landed and posted this here
Чтение данного поста — часть процесса изучения инструмента. О каком нежелании идет речь?
UFO just landed and posted this here
git — это инструмент для опытных людей, и в нем есть немало способов выстрелить себе в ногу: раз, два
UFO just landed and posted this here
На мой взгляд ситуация относится уже к advanced usage, нежели daily workflow. Беглый поиск в рунете по этой теме вообще ничего не дал.
UFO just landed and posted this here
Пассаж про Рунет относится к тому, что Хабр русскоязычный ресурс и я не вижу ничего плохо, что описание и решение подобной ситуации теперь есть и на русском.

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

Я разрулил конфликты. закончил rebase и был уверен что пуш ушел, потому что в исходящих комитах было пусто. Я это проделывал сотню раз. О том, что rebase может перескочить комит я узнал только спустя лет 5 пользования Гитом.
Вы только не забывайте, что когда-нибудь гит сделает GC, и такие ваши коммиты, которых нету ни в одном бранче, уничтожатся навсегда. Так что не особо часто надейтесь на эту фичу.
Вообще говоря, те коммиты, которые еще можно найти через reflog, не удаляются при стандартном git gc

Ну и общее правило по поиску потеряных вещей — чем раньше спохватишься, тем вероятнее найти — тут тоже работает.
Автоматический GC прекрасно выключается с помощью git config --global gc.auto 0. И под сборку мусора коммиты попадут только через 30 дней, если не выкручивать никаких ручек вроде gc.reflogExpireUnreachable.
UFO just landed and posted this here
UFO just landed and posted this here
Еще одна статья о том, что git rebase — зло. Почему вы все еще его используете?
Я rebase использую буквально каждый день и вижу только добро.
А лопухи, которые не понимают что делают, пускай пеняют на себя.
Можно поинтересоваться — зачем вы rebase используете каждый день?
1. нравится мне линейная история: если фикс мелкий (т.е. не фича-бранча), то перед пушем делаю git pull --rebase, чтобы избежать мерж коммита.
2. когда долго работаю в своей ветке то ребэйс делаю чтобы получить свежие изменения (если что-то критичное починили)
3. если я быстро заметил баг в предпоследнем коммите (и еще не пушал), то коммичу фикс, делаю git rebase -i HEAD~3 и объединяю с тем коммитом в котором я баг добавил. Это чтобы не дефрагментировать историю тайпо-фиксами.
Вообще конечно большой плюс гита в том, что потерять коммит практически нереально. Конечно, если не сделать агрессивный gc сразу после. Коммит всегда будет где-то, и через reflog ли, или просто поиском объекта, но этот коммит можно найти и использовать.
Потому коммитить надо часто и не стесняться мерджить и ребейзить.
Однажды я сильно не выспавшись занялся любимым делом, и по запарке случилось git stash, что-то ещё и наконец git stash clear (коммита не было). Доставал как-то так:
git fsck --unreachable | awk '{print $3}' | while read hash; do echo $hash; git show $hash | grep 'тут кусок - разница'; done

Показал мне кучу хэшей, но за искомым хэшем вывел строку, заданную в последнем grep. Так я взял нужный хэш, по которому успешно всё восстановил, закоммитил и запушил.
С тех пор стал стараться делать коммиты меньше и чаще.
Рекомендую всегда делать rebase -i, чтобы убедиться, что перенесены будут именно ожидаемые коммиты.
Вот только что видел проблему в команде именно с rebase -i.
Верхний коммит — изменение и одновременно нужный merge. git rebase -i показывает совершенно другие коммиты — видно все свои изменения выкинуты.
С широко раскрытыми глазами редактор закрывается без единого изменения (в попытке убрать этот кошмар), и git спокойно (и радостно для наблюдающих) так и делает всю ветку.

Потом восстанавливали примерно как тут написано.
> редактор закрывается без единого изменения (в попытке убрать этот кошмар)

А откуда взялась идея, что отсутствие изменений == отмена rebase? :) Это вообще-то то же самое, что и просто rebase, без -i
Для отмены операции нужно текстовый буфер в редакторе очистить и сохранить, тогда git обидится на отсутствие коммитов и ничего делать не будет.
P.S. rebase своеобразно сочетается с merge commit — в обычном логе последний показывается как один коммит, но в списке rebase -i будут указан полный список коммитов второго родителя.
Если вы используете Vim, то можно ничего не удалять, а написать :cq<CR>: тогда git будет считать, что он не смог запустить редактор и отменит rebase (:cquit заставляет Vim выйти с кодом возврата 1).
Прочитав статью я порадовался, что мой линукс (а соответственно и гит на нем) лежит на виртуалке в VirtualBox, а разработку веду на phpStorm (с синхронизацией по sftp) на винде в папке под дропбоксом.
Потерять код невозможно в принципе. Если что-то случится с линуксом или гитом, всегда есть актуальная копия в винде, если что-то потеряно в винде, всегда можно восстановиться из дропбокса.
При грамонтном использовании гит — потерять код тоже невозможно.
Как выше писали, автор сам виноват, что появилась такая проблема.
И у вас могла быть такая же проблема.
И дробокс тут не сильно бы помог, ибо лазать по истории в десятках файлах и выуживать оттуда данные сложнее, чем из гит-а.
>> При грамонтном использовании гит — потерять код тоже невозможно.

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

>> И у вас могла быть такая же проблема.

Ну была бы, и что. Удалил бы локальный репозиторий, заново склонировал удаленный репозиторий и синхронизировал с папкой на винде. Там-то код не потерялся.

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

Ну это еще спорный вопрос. И все же они там есть, и это главное.
Я взял за практику делать git stash перед git pull.
А когда git pull завершился, я делаю git stash apply.
Если тут возникают конфликты, исправляю.

В итоге у меня мой коммит единственный и идёт всегда после мерджа с master'ом.
угу, давно сделал себе алиас git up:
up = !(git add . && git stash && git pull --rebase >&2) | grep -v 'No local changes to save' && git stash pop
(впрочем это для тех кто знает как резолвить конфликты)
Для этого есть git rebase --autostash и соответствующая настройка rebase.autostash. Я, правда, не в курсе, когда это появилось. У меня версия 1.8.5.5.
SmartGit и делает stash перед каждым потенциально критичным действием. Проблема была в том, что stash работает только с незакоммиченными файлами. У меня же уже был сделан коммит, который git успешно откатил без следа во время неудачной попытки сделать ребейс.
Вот из-за такой х__ни я держу исходники всех проектов в Dropbox с проплаченным Extended Version History (бывший PackRat).

Это позволяет откатиться на любую версию любого файла.
И, кстати, вот как себя ведёт mercurial при rebase/histedit/любая другая команда, удаляющая изменения: он вам явно прямо в консоли пишет, где сохранил резервную копию старых коммиттов и не удаляет их автоматически (git иногда сам запускает git gc, после него вы бы ничего не нашли). Авторы git же считают, что про git reflog надо было читать, до того, как вы написали git rebase, а запуск git gc --auto после rebase — это вообще свинство.
В чем же свинство? git gc --auto не чистит коммиты, доступные из рефлога (при стандартных настройках)
Не знал. Я привык в reflog видеть сотни коммитов, поэтому полагал, что без чистки коммитов из reflog’а git gc не слишком полезен.
Правда, где «сотни коммитов» это много — там gc практически не запускается. Но это большинство проектов с моим участием, так что такая мысль не слишком удивительна.
Если уж точно, то он чистит сам reflog от старых записей (см. настройки gc.reflogExpire [90 дней по умолчанию] и gc.reflogExpireUnreachable [30 дней по умолчанию]).
А потом и удаляет старые объекты, на которые нет ссылок (см. gc.pruneExpire [2 недели по умолчанию]).
> То ли баг в Гите, то ли мои неправильные действия, которые я в запарке не заметил.

за последние 5 лет я ни разу не терял коммиты в git. ЧЯДНТ?
за последние 5 лет я ни разу не терял коммиты в git. ЧЯДНТ?

Скорее всего проблема в том, что вы используете гит как надо, согласно документации.
а зачем вы его используете не как надо? не по документации то бишь…
Ну как зачем? Чтобы терять коммиты и ловить так называемые «баги в git» вместо того, что бы наслаждаться изяществом и простотой сей vcs.
Sign up to leave a comment.

Articles