Сходство и различие между Mercurial и Git

По роду своей деятельности я нередко становлюсь свидетелем «священных войн» между коллегами-программистами на тему, какую же систему контроля версий выбрать для того или иного проекта. Роль системы контроля версий особо остро ощущается в случаях разработки и поддержки проектов с длинной историей. Вариантов инструментов много, но я хочу сконцентрироваться на двух, на мой взгляд, наиболее перспективных: Mercurial и Git. Далее попробуем рассмотреть возможности обеих систем с позиции их внутреннего устройства.

Немного истории


Толчком к созданию обеих систем, как Mercurial, так Git, послужило одно событие 2005 года. Всё дело было в том, что в упомянутом 2005 году ядро системы Linux потеряло возможность бесплатного использования системы контроля версий BitKeeper. После пользования BitKeeper в течение трёх лет разработчики ядра привыкли к его распределённому рабочему процессу. Автоматизированная работа с патчами сильно упрощала процесс учёта и слияния изменений, а наличие истории за продолжительный период времени позволяло проводить регрессию.

Другой немаловажной частью процесса разработки ядра Linux стала иерархическая организация разработчиков. В вершине иерархии стоял Диктатор и много Лейтенантов, отвечавших за отдельные подсистемы ядра. Каждый Лейтенант принимал или отклонял отдельные изменения в пределах своей подсистемы. Линус, в свою очередь, затягивал их изменения и публиковал их в официальном хранилище ядра Linux. Любой инструмент, вышедший на замену BitKeeper, должен был реализовывать такой процесс.

Третьим критичным требованием к будущей системе была скорость работы с большим количеством изменений и файлов. Ядро Linux — это очень большой проект, принимающий тысячи отдельных изменений от тысяч различных людей.

Среди множества инструментов подходящего не нашлось. Практически одновременно Мэт Макол (Matt Mackall) и Линус Торвальдс (Linus Torvalds) выпускают свои системы контроля версий: Mercurial и Git соответственно. В основу обеих систем легли идеи появившегося двумя годами ранее проекта Monotone.

Cходство


Обе системы контроля версий имеют ряд общих черт:
  • ревизии ассоциируются с контрольными суммами;
  • история имеет вид направленного ациклического графа;
  • поддерживаются высокоуровневые функции, в т.ч. бисекция, ветвление и выборочные фиксации.

Отличия


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

Хранение истории

И Git, и Mercurial идентифицируют версии файлов по их контрольной сумме. Контрольные суммы отдельных файлов объединяются в манифесты. В Git манифесты называются деревьями, в которых одни деревья могут указывать на другие. Манифесты непосредственно связаны с ревизиями/фиксациями.

Mercurial для улучшения производительности пользуется специальным механизмом хранения Revlog. Каждому файлу, помещенному в хранилище, сопоставляется два других: индекс и файл с данными. Файлы с данными содержат слепки и дельта-слепки, которые создаются только когда количество отдельных изменений файла превышает некоторое пороговое значение. Индекс служит инструментом эффективного доступа к файлу с данными. Дельты, полученные в результате изменения файлов под контролем версий, добавляются только в файлы с данными. Для того, чтобы правки из разных мест файла объединить в одну ревизию, используется индекс. Ревизии отдельных файлов складываются манифесты, а из манифестов — фиксации. Этот метод зарекомендовал себя весьма эффективным в деле создания, поиска и вычисления различий в файлах. Также к достоинствам метода можно отнести компактность по отношению к месту на диске и достаточно эффективный протокол передачи изменений по сети.

В основе модели хранения Git лежат большие объектные бинарные файлы (BLOB). Каждая новая ревизия файла — это полная копия файла, чем достигается быстрое сохранение ревизий. Копии файлов сжимаются, но, всё равно, имеют место большие объёмы дублирования. Разработчики Git применили методы упаковки данных для снижения требований к объёму хранилища. По существу они создали нечто похожее на Revlog для указанного момента времени. Полученные в результате упаковки пакеты отличаются от Revlog’а, но преследуют ту же цель — сохранить данные, эффективно расходуя дисковое пространство. В виду того, что Git сохраняет слепки файлов, а не инкремент, фиксации могут легко создаваться и уничтожаться. Если при анализе требуется посмотреть разницу между двумя различными фиксациями, то в Git разность (diff) вычисляется динамически.

Ветвление

Ветвление — очень важная часть систем управления конфигураций, т.к. оно позволяет проводить параллельную разработку новой функциональности, сохраняя стабильность старой. Поддержка ветвления присутствует как в Git, так и в Mercurial. Отличия формата хранения истории нашли своё отражение и в реализации ветвления. Для Mercurial ветка — это некая отметка, которая прикрепляется к фиксации навсегда. Эта отметка глобальна и уникальна. Любой человек, затягивающий изменения из удалённого хранилища, увидит все ветки в своём хранилище и все фиксации в каждой из них. Для Mercurial ветки — это публичное место разработки вне основного ствола. Имена веток публикуются среди всех участников, поэтому в качестве имен обычно используют устойчивые во времени номера версий.

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

Практические аспекты использования


Различия в реализациях Git и Mercurial можно проиллюстрировать на примерах.

Mercurial позволяет легко фиксировать изменения, проталкивать и вытягивать их с поддержкой всей предыдущей истории. Git не заботится о поддержке всей предыдущей истории, он только фиксирует изменения и создаёт указатели на них. Для Git не имеет значения предыдущая история и на что раньше ссылались указатели, важно то, что актуально в текущий момент. Существует даже инструмент, гарантирующий сохранность локальной истории при вытягивании изменений из внешнего хранилища — fast-forward merge. Если этот механизм включен, то Git будет сообщать об изменениях, которые не могут быть улажены без продвижения по истории вперёд. Данные ошибки можно не принимать во внимание, если поступившие изменения ожидались.

При выполнении отката фиксации или затягивания со слиянием Git просто меняет указатель ветки на предыдущую фиксацию. В действительности в любой момент времени, когда требуется откатиться в некоторое предыдущее состояние, Git ищет в логе соответствующую контрольную сумму и сообщает какая фиксация ей соответствует. Как только что-то зафиксируется в Git, то всегда можно к этому состоянию вернуться. Для Mercurial существуют случаи, когда невозможно полностью вернуться в исходное состояние. Т.к. Mercurial для решения какой-либо проблемы создает фиксацию, то в некоторых случаях затруднительно переместиться назад с учётом свежего изменения.

Для решения различных проблем в Mercurial существуют расширения. Каждое расширение решает свои проблемы хорошо, если существует само по себе. Существует даже некоторые расширения, обеспечивающие сходную функциональность, но разными способами.

Для примера рассмотрим работу с отложенной историей. Допустим, нам необходимо записать изменения из рабочей копии без фиксации в хранилище. Git предлагает использовать stash. Stash — это фиксация или ветка, которые не сохраняются в обычном месте. Stash не показывается, когда выводится список веток, но всеми инструментами он трактуется как ветка. Если аналогичная функциональность требуется Mercurial, то можно использовать расширения attic или shelve. Оба этих расширения хранят «отложенную» историю в качестве файлов в хранилище, которые могут быть при необходимости зафиксированы. Каждое расширение решает проблему немного по-своему, поэтому имеет место несогласованность форматов.

Другой пример, команда git commit --amend. Если нужно изменить самую последнюю фиксацию, например, добавить что-нибудь забытое или изменить комментарий, то команда git commit --amend создаст полностью новый набор файловых объектов, деревьев и объектов фиксации. После этого обновляется указатель ветки. Если далее потребуется откатить изменения, то необходимо только вернуть указатель на предыдущую фиксацию командой git reset --hard HEAD@{1}. Чтобы повторить это в Mercurial потребуется откатить фиксацию, затем создать новую, далее импортируем содержимое последней фиксации при помощи расширения queue, дополняем её и делаем новую фиксацию.

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

Выводы


В последнем разделе этой статьи хотел бы высказать собственное мнение по выбору системы контроля версий. И Mercurial, и Git хороши в своих сегментах.

Например, для целей ведения коммерческого программного проекта мне больше импонирует Mercurial.
  • Строгая работа с историей в Mercurial гарантирует возможность учёта и поиска первоначального источника ошибки.
  • После слияния с веткой в Git мы рискуем получить гига-патч, в котором где-то будет скрываться ошибка.
  • Глобальные ветки также дают возможность контроля за работой коллег при регулярной синхронизации с центральным хранилищем.

Для хранения бинарных файлов, например, электронной библиотеки, Git подходит лучше. По сравнению с Mercurial он не ориентирован на расчет дельты файлов, что для бинарного содержимого не очень эффективно. Сами файлы меняются редко, а основные операции с ними — это перемещение и добавление. По моим собственным наблюдениям папка хранилища Git с историей моей библиотеки сопоставима по размерам с рабочей копией с окрестностью примерно 10%.

Источники знаний


  1. Основной источник
  2. Описание формата Mercurial
  3. Описание формата Git
  4. Общая справочная информация из Wikipedia

Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 404
  • +2
    Так что же всё-таки лучше? :)
    • НЛО прилетело и опубликовало эту надпись здесь
      • +25
        А чего не CVS?
        • НЛО прилетело и опубликовало эту надпись здесь
          • +11
            Visual SourceSafe!
            • +5
              ftp!
              • +1
                telnet, чо уж…
                • +2
                  echo $'USER vasya\nPASS qwerty\nPASV\nSTOR...' >/dev/tcp/$host/21 тогда уже, господа извращенцы.
                  • +2
                    Дискеты в шкафу
                    • +1
                      Бобины.
                      • +1
                        Зубилом по камню.
                        • +23
                          Вилами по воде
                          • +3
                            Главное — бекап не забыть сделать
                            • +1
                              На перфоленту.
                              • +12
                                Переписать код в тетрадку!
                                • +2
                                  С воды, по которой писано вилами, я правильно понял?

                                  Ребята, you made my day!
                                  • 0
                                    Главное потом скомпилировать и запустить из тетрадки.
                                    • 0
                                      Код в тетрадку… ЕГЭ вспомнилось…
                                      Привет из 2017.
                  • 0
                    Ага. Как раз занимался переносом истории из StarTeam 6.0 в git. Удовольствие то ещё :)
                    • 0
                      Да весь ST — одно сплошное «удовольствие».
                    • +1
                      Кто-то его ещё использует? Разработка через страдание.
                    • +1
                      accurev наше всё Ж)
                    • +1
                      Если бинарные файлы хранить, то про SVN согласен, а если текстовые, то нет. Для текстовых все же Hg, Git лучше )
                      • 0
                        А зачем бинарные в СВН хранить?
                        • +1
                          К примеру MP3, они редко меняются и не компилируются. При таких ситуациях можно поступить двумя способами:
                          1) Положить файл на какую-либо шару, а в каком-либо сборочном скрипте указать где его брать
                          2) Положить непосредственно в CVS

                          Второй способ лучше:
                          1) Знаете какой из «умных людей» удалил столь нужный файл!
                          2) Нет такой ситуации, когда Вы не знаете при решении какой задачи потребовался этот файл?
                          3) Админам, как правило проще бэкапить

                          Банально: about.png это текстовой файл? ;)
                          • –1
                            Нет, вы не поняли. Зачем в Svn, а не в Git?
                            • +4
                              А вы работали с большими репозиториями git? :-) Гит не тянет больших репозиториев, зачастую просто падает или дико тормозит. Большие — это в несколько гигов на самом деле.
                              А кроме того, никто не умеет нормально мержить бинарники, да и возможность вытащить часть репозитория очень полезна бывает.
                              Так что код — в git (или hg), а данные -в svn!
                              • +3
                                Про бинарники — у гита есть annex, у hg — largefiles. Отлично работают оба, правда надо запомнить пару допкоманд
                                • 0
                                  Ну есть ещё и git media, я и сам нечто подобное реализововывал. Но это уже примочки, со всеми их минусами. Да и вытянуть быстро кусок репозитория из git-a проблематично.
                                  Так что не убедили :-)
                                • +1
                                  Вот кому надо статьи про Git и Mercurial писать )
                                  • +3
                                    Вы мне льстите, и я ничего не знаю про mercurial.
                                    Вообще у меня была идея написать «феерическое расставление точек на git» по поводу практики работы с ним в продакшене софтверных компаний, но не хватает знаний.
                                • 0
                                  Потому-что GIT будет локально хранить весь репозиторий, что увеличивает необходимое пространство минимум вдвое.
                            • 0
                              Да ладна! Бинарники в SVN? Почитайте, SVN не следит за целостностью данных. Вы можете положить туда бинарник, а через год вытащить его оттуда битым. У меня на практике бились картинки и шрифты. Так что бинарные файлы так же лучше хранить в Git или Hg.
                              • +1
                                Пруф?
                                Конечно есть там чексуммы.
                                • –2
                                  Не пользуюсь SVN уже года три-четыре. Чексуммы есть, однако он их не проверяет, когда выгружает файлы. О том что файл битый узнаешь позже. Да и стоит ли сравнивать надежность хранения данных у централизованной и децентрализованной системы?
                                  • +2
                                    Пруф что не проверяет?

                                    Битый файл мог быть у вас потому что вы его туда отправили битым (битая память?). В противном случае можно было с нескольких попыток получить целый файл.

                                    А поповоду надёжности — попробуйте забэкапить git репозиторий в файлы, инкрементальным бэкапом, все бранчи.

                                    (в файлы — это в смысле — при условии что целевой бэкап будет где-нибудь на S3 храниться, и прав на удаление/изменение файлов не будет, только на добавление — ведь именно так нужно настраивать права чтобы какой-нибудь злоумышленник не стёр и бэкап при компрометации машины)
                                    • 0
                                      Пруф что не проверяет?

                                      Ну да, проект был задеплоен год назад с битыми файлами. Если бы это был единичный случай, то ладно. С гитом за 4 года таких проблем не было.

                                      Любой клон гита — это уже бекап. Для того, чтобы забекапить на том же S3 достаточно синхронизировать все ветки. И если история не будет переписываться, то файлы будут только добавляться.
                                      • 0
                                        Ну это не показатель что файлы были битые, может баг в svn в вашей ОС/версии, может проблема с железом.
                                        Любой клон гита — да тоже бэкап.

                                        А вот бэкап копированием папки git — после git gc всё будет перезаписываться. К тому же чтобы он был в 100% случаев консистентным тоже нужно постараться вроде как.

                                        Сделать такой бэкап с помощью git bundle — это нужно постараться. И писать это не на bash. И желательно с unit тестами. При этом метки бранчей-тэгов не будут «инкрементальными» (впрочем они не много места занимают)
                                        • +1
                                          Гит крякает, если файл поврежден. У него механизм хранения данных такой. Он файлы внутри папки .git называет согласно их контрольной суммы.

                                          Бренчи и теги практически ничего не весят, это метаданные. А инкрементальными бекап можно получить если, например, пушить на север бекапа по крону (или наоборот пулить с сервера бекапа). Причем без параметра --force он ничего не перезапишет. И еще советую почитать про параметр --mirror.

                                          git bundle удобен для разового переноса. В принципе это создание данных для push перед отправкой на сервер.
                                          • +3
                                            Бекап на самом деле запросто реализуется — открываете архивируемый репозиторий наружу и делаете на другом конце git clone --mirror --bare — и потом только фетчите ветки.
                                            • +1
                                              В принципе я это и пытался объяснить :)
                                              • 0
                                                Пытались то пытались, но это было всё совершенно не по теме что я поднял, а другие способы бэкапа.
                                              • 0
                                                В условиях было — бэкап в файлы. На другом конце пассивная информация, в ней нельзя запустить гит.
                                                • 0
                                                  Если есть способ примонтировать с помощью fuse, то как раз по теме. Выполняйте пуш.
                                                  • 0
                                                    Не совсем понял, видимо, что вы хотите бэкапить? Репозиторий с версиями? Тогда используйте описанный способ. Как вы хотите сохранять версионность, не запуская систему контроля версий-загадка для меня.
                                                    Если же вы хотите бекапить просто набор файлов некоторого снапшота: git archive --help
                                                    • 0
                                                      Point был в том что в SVN — svndump может делать инкрементальные бэкапы, который можно восстановть в полноценный репозиторий, причём место назначение бэкапов может быть что угодно — лента, флэшка, Amazon Glacier, медленный FTP, удалённый сервер (доступный по протоколам передачи файлов) без права стирания/перезаписи файлов.

                                                      Git bundle же может делать аналогичное только если 1 бранч. (если много бранчей то это очень сложно).

                                                      p.s.
                                                      про другие способы бэкапов git'а я знаю, спасибо.
                                                      • 0
                                                        Git bundle же может делать аналогичное только если 1 бранч. (если много бранчей то это очень сложно).

                                                        Поищите в справке параметр --all и сложность отпадет.

                                                        Другие способы бекапа — pull/push в удаленный bare репозиторий. Если на удаленном сервере не стоит git, то папку с репозиториями можно монтировать в файловую систему с помощью fuse и запускать нужную операцию гита на локальном сервере.
                                                      • 0
                                                        Для примера, у меня есть реп, которые я таскаю на galaxy s3. Ничего сверхважного, скрипты, но храню я их там в гите. Хоть на андроид и есть клиенты и серверы гита, но я их не использую. Данные вынимаю и сохраняю с помощью стационарного гит клиента.
                                              • 0
                                                И, кстати, если при работе крона будет ошибка (не может выполнить push/pull), это будет означать, что коммиты переписывались. И либо это ошибка фс, либо кто-то намеренно их переписал. В любом случае факт модификации будет отловлен.
                                            • 0
                                              Про проверку целостности — где то читал, где уже не помню. Про битые файлы — реальный факт, проблема была с очень большим репозиторием (4 гигобайта). И в сети тоже это обсуждалось. Быстрый поиск в гугле результатов не дал.
                                      • +5
                                        Для бинарных файлов у HG есть специальное расширение largefiles.
                                        Оно встроено начиная с версии 2.0.

                                        • 0
                                          Спасибо, не знал! ;) А еще можете посоветовать нормальный не монстроподобный issue-трекер из тех что пробовали(если пробовали) и который понравился?
                                          • 0
                                            А issue-трекер у bitbucket пробовали?) Он даже слишком простой.
                                            • 0
                                              Извините что не уточнил нюанс. Мне нужно на локальной папки, т.к. проект веду без использования внешнего интернет-сервиса. Обхожусь только локальной папкой ".hg" расположенной в TrueCrypt контейнере :) Устал использовать TaskCoach. Пока у меня все выглядит так: после удаления\дабавления\изменения задач я делаю hg ci -m «added new task». Это как-то уже напрягает (
                                              • 0
                                                Если под винду, посмотрите ToDoList.
                                                • 0
                                                  Чем от будет отличаться от упомянутого мною TaskCoach? Ответ: Ничем!
                                                  • 0
                                                    Ну я же не знаю, что именно вас утомило в использовании TaskCoach. Вы бы описали, чем вас TaskCoach не устраивает и какие у вас требования к issue-трекеру вообще.
                                                    • 0
                                                      Ок. Опишу один use-case по добавлению задачи, как это сейчас выглядит у меня:
                                                      1) Открыть в TaskCoach docs\projects.tsk
                                                      2) Добавить задачу
                                                      3) Cохранить
                                                      4) В виду того что файл docs\projects.tsk изменился выполняем команду в консоли: hg ci -m «added new task»

                                                      Если закрываем задачу, тоже самое. Если изменяем приоритеты — тоже самое.

                                                      Я бы хотел что-нибудь похожее на такой use-case:
                                                      1) hg task-add «Зарефактори ClassName.super_method()»
                                                      2) hg ci -m «added new task»

                                                      еще лучше чтобы п.1 и п.2 в одном.

                                                      Если надо посмотреть что есть:
                                                      hg task-list
                                                      выводится список имеющихся не закрытых задач

                                                      Если надо закрыть задачу, то:
                                                      hg task-close номер_задачи

                                                      Это пример того что я хочу.
                                              • 0
                                                BugsEverywhere попробуйте. Интересная штука.
                                            • 0
                                              redmine
                                      • 0
                                        Лично я в свое время выбрал для себя Git, так как он более распространен. Хотя на всякий пожарный и Mercurial освоил.
                                        • НЛО прилетело и опубликовало эту надпись здесь
                                          • 0
                                            Я тоже начал со Ртути, т.к. ГуглоКод, а потом перешёл на Гит, т.к. ГитХаб
                                            • +2
                                              А я на гитхаб коммичу через hg.
                                      • +1
                                        Ничего не лучше, кому что нравится то и используют.
                                        Не хватало ещё и холивара по поводу Git VS Mercurial :D
                                        • 0
                                          Интересно, возможно ли в Mercurial переключать ветки в рамках одной рабочей копии репозитория?
                                          Для проектов на C++, где время сборки может быть большим это иногда важно.
                                          • 0
                                            Если я вас правильно понял, то можно просто создать новую директорию и в ней симлинк на ваш .git
                                            Ну а дальше git checkout ваш_бранч, или использовать тот же самый бранч, это уже как нравится
                                            • 0
                                              С git как раз пониматно. Сделал git checkout, пофиксил/смёрджил/запушил, сделал назад checkout. Интересно, можно ли так в mercurial, или там обязательно клонировать в отдельный каталог.
                                              • +1
                                                С hg все аналогично. Сделал hg up branch1, пофиксил/померджил, сделал hg up branch2.
                                              • 0
                                                Зачем такие сложности? Достаточно сделать клон из уже имеющейся папке. В файловых системах, где поддерживаются жесткие ссылки (ntfs, ext) — будут задействованы именно они, при дублировании папки .git. По окончанию работы — удалить клон.
                                                • 0
                                                  Если мне нужно исправить пару файлов в большом проекте (и ветки близки, как в случае hotfix и new feature development), то клон в новое место приведёт к тому, что бинарники «протухнут», и придётся делать полную пересборку. Переключение в одной копии позволит сделать быструю инкрементальную досборку.
                                                  • 0
                                                    Можно сделать и клон, но на сколько я понял как раз не хотелось делать клон. Возможно я что то не так понял.
                                                    Тем не менее, при работе склоном нужно будет делать push в оригинал, и потом с оригинала на сервер, конечно мы так же можем поправить и конфиг, но тогда надо будет кидать комиты через сервер, что нам точно не нужно.

                                                    С симлинком, можно работать даже с одной версией и отправлять на сервер из любой папки. И Stage файлы будут видны в обоих, так же как и комиты, без лишних pull/push, но с checkout и ресет…

                                                    Тут уж очень сильно зависит от того что нужно делать. А там хоть клон, хоть симлинк, да хоть полная копия .git
                                                • –2
                                                  Можно
                                                  • 0
                                                    Ну напишите человеку как? Возможно это пригодится, так же кому то еще.
                                                  • +3
                                                    hg update {revision} (он же hg up, hg checkout, hg co); где {revision} может быть в том числе и веткой.
                                                    • 0
                                                      А можете немного поподробнее объяснить, что вы имели в виду?
                                                      • +1
                                                        Думаю мне уже ответили. Имелось ввиду возможность параллельной работы над разными ветками в одной рабочей копии. Если две ветки не сильно разошлись, то переключение из одной в другую изменит только малое количество файлов (важно чтобы timestamps при этом не менялись), и инкрементальная сборка проекта останется быстрой.Похоже это можно сделать и в git, и в mercurial.
                                                      • 0
                                                        переключиться на другую
                                                        > hg up <branch-name>

                                                        посмотреть бранчи:
                                                        > hg branches

                                                        дать имя бранчу:
                                                        > hg branch <new-branch-name>

                                                        закрыть бранч:
                                                        > hg ci --close-branch -m «branch is close»

                                                        мержим текущий бранч с другим:
                                                        > hg merge <branch-name>
                                                        не забудем тут же закомитить мерж:
                                                        > hg ci -m «branches merge»

                                                        тута:
                                                        -m «это указание сообщения коммита»
                                                      • –10
                                                        Зато git написал лично Линус Торвальдс!
                                                        • –17
                                                          Не понимаю чем может не устраивать git. Единственное объяснение выбора в пользу другой CVS я вижу только в незнании git
                                                          • –1
                                                            А как в нем изменить сообщение (например, исправить очепятку в тексте) у древнего коммита, который был давным давно запушен? ;)
                                                            • +2
                                                              Это киллер-фича mercurial?
                                                              • +5
                                                                Это киллер-фича Subversion.
                                                              • +4
                                                                rebase.
                                                                • +1
                                                                  rebase — не катит, так как все комиты буду заново пересозданы.

                                                                  В случае же, если проект на столько критичен, что нужно волноваться об одной опечатке в описании, возможно есть смысл иметь модерацию комитов перед мержем в основную ветку. Это так же позволить повысить качество кода, и иметь комиты только по делу.
                                                                  • 0
                                                                    rebase — не катит, так как все комиты буду заново пересозданы.
                                                                    Ну иначе-то никак. А юзеру нужно.
                                                                    • +2
                                                                      То есть вы готовы запороть всю историю и иерархию комитов, ради одной опечатки?
                                                                      • +5
                                                                        Я — нет. А сферическому юзеру в вакууме может быть очень нужно
                                                                        • +4
                                                                          Зачет. Однако здешние обитованцы не заценили юмора…
                                                                        • +3
                                                                          Описание изменения и его родители используются для вычисления хэша что в git, что в mercurial. Соответственно изменить сообщение, не «запоров» историю (точнее, не пересчитав хэши всех потомков) невозможно.
                                                                          • +1
                                                                            О том и речь. Здесь только премодерация комитов может помочь.
                                                                          • +1
                                                                            vovkab, а что, rebase действительно запорет всю историю и патриархию? Свят-свят.
                                                                      • 0
                                                                        > rebase — не катит, так как все комиты буду заново пересозданы.

                                                                        Предположим что это не критично, как это сделать? Просто когда я искал способ это сделать, то единственным рабочим решением оказалось использовать git filter-branch, в котором проверять хеш коммита и заменять текст. Буду рад увидеть лучшее решение :)
                                                                        • +1
                                                                          Если это локальная ветка и данные еще не в паблике, то конечно же через rebase.
                                                                          Вот тут подробнее: git-scm.com/book/ch6-4.html
                                                                      • +3
                                                                        Создать ещё один коммит, это ж система контроля версий ёбанаврот!
                                                                      • +24
                                                                        Зато объяснений в пользу выбора Git целых два:
                                                                        1. Мода/популярность
                                                                        2. незнание других DVCS
                                                                        • –25
                                                                          А в пользу Mercurial только один (и кстати вы не первый кто в этом признается): «Не осилил Git»
                                                                          • +13
                                                                            Ну действительно, разве могут быть у нормального человека рациональные аргументы против Git.
                                                                            • +31
                                                                              Нет. У git нет API, позволяющего нормальную интеграцию с чем‐либо (делать каждый раз fork-exec и читать выхлоп из pipe — это очень медленно, как в смысле скорости разработки так и скорости исполнения. Даже несмотря на то, что интеграция пишется на языке, который успешно прячет fork-exec).

                                                                              У git нет огромного количества хуков (например, preoutgoing, все pre-{command}, …).

                                                                              У git нелогичное распределение функциональность по командам: checkout создаёт ветки или обновляет дерево, push отправляет изменения или удаляет их на том конце, diff может показывать изменения между ревизиями, а может — status, причём сам status этого не умеет…

                                                                              У git иногда плохая обработка аргументов командной строки (pull принимает сначала аргументы для merge, затем для fetch).

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

                                                                              Аналог revsets у git обладает куда как меньшими возможностями.

                                                                              Многие команды git, показывающие список ревизий, не имеют возможности настройки формата вывода.

                                                                              У часто используемых команд вроде push/pull есть ненужные обязательные аргументы: «git push origin :branch».

                                                                              У git нет аналога «hg incoming».

                                                                              У git нет удобного аналога «hg grep».

                                                                              Большую часть претензий можно игнорировать или терпеть. Но не первые две.
                                                                              • +1
                                                                                У git нет API, позволяющего нормальную интеграцию с чем‐либо
                                                                                строго говоря, есть — libgit2 называется. Но разрабатывается оно независимо от апстрима.
                                                                                • +3
                                                                                  Ещё и dulwich. Я знаю и использую libgit2. Но стабильного релиза ещё нет (правда, обещали, что следующий релиз — 1.* и со стабильным API, но сроки неизвестны), а binding’и к python (pygit2) не обладают всеми возможностями даже libgit2, сама libgit2 не обладает всеми возможностями git.

                                                                                  Кроме того, это независимый проект. Если человек использует git, то консольный клиент у него окажется почти наверняка, а вот libgit2 — нет. А вот с mercurial и bazaar не так — если он есть, то почти наверняка установлен «нормально», т.е. доступен для импорта в python. К subversion тоже зачастую прилагается C‐шная библиотека (официальная!). К git не прилагается ничего.
                                                                                  • 0
                                                                                    А еще есть JGit (дада, на Java).
                                                                                  • 0
                                                                                    У часто используемых команд вроде push/pull есть ненужные обязательные аргументы: «git push origin :branch»

                                                                                    SYNOPSIS git push [--all | --mirror | --tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream] [<repository> [<refspec>...]]

                                                                                    У меня работает именно так, как в описании, то есть ни repository, ни refspec не являются обязательными, у них есть значения по умолчанию.
                                                                                    Но лично я их чаще всего в явном виде прописываю, чтобы самому себе точно показать что и куда я push'у, например.
                                                                                    • +1
                                                                                      %  git add
                                                                                      Nothing specified, nothing added.
                                                                                      Maybe you wanted to say 'git add .'?
                                                                                      .
                                                                                      • 0
                                                                                        Ага, я тоже после ртути поначалу плевался. А еще staging считал жутко неудобной никому не нужной хренью. :)
                                                                                        Но потом заметил, что перестал случайно добавлять всякую ерунду в коммиты, и плеваться закончил.
                                                                                        • 0
                                                                                          перестал случайно добавлять всякую ерунду в коммиты
                                                                                          post hoc ergo propter hoc, что ли?
                                                                                          • +1
                                                                                            Для этого надо что-то доказывать, а я просто рассказываю свой опыт и мнение.
                                                                                            • +2
                                                                                              Ну тогда и я расскажу. Лично мне staging area помогает раз, редко два раза в месяц. Остальное время путается под ногами. А ерунда не коммитится и так.
                                                                                          • +1
                                                                                            Неудобно и ненужно. У меня просто vim открывает diff при фиксации изменений в соседнем окне и я делаю review. Никакая staging area никак не повлияет на возможность добавить скопом все изменения без review и тестирования, она только раздражает необходимостью писать на одну команду больше.
                                                                                            • 0
                                                                                              я может что-то не понимаю, но почему не использовать git commit -a
                                                                                              • +3
                                                                                                «git commit -a» не делает addremove. Но это не важно. «Неудобно» относится к тому, что возможность наличия чего‐то в staging area необходимо учитывать. Например, при добавлении нового файла в репозиторий с помощью «git add» «git diff» не покажет никаких изменений. Причина мне понятна, но необходимость помнить о ней неудобна.
                                                                                                • 0
                                                                                                  А. я вас пониял. Я просто привык, возможно, это действительно неудобно.
                                                                                            • +1
                                                                                              Концепции staging в ядре меркуриал нет, но из коробки есть расширение mq, которое даёт вам staging и даже более крутой (многослойный)!
                                                                                              pqr7.wordpress.com/2011/01/07/a-git-users-guide-to-mercurial-queues/
                                                                                              • 0
                                                                                                В 2.1 появились фазы. В 2.5 появилась эволюционирующая история.
                                                                                          • 0
                                                                                            А теперь удалите ветку, не указав, откуда вы её хотите удалить. Я не про возможность не указать оба или последний из этих аргументов. Я про невозможность не указать только первый, указав второй.
                                                                                          • 0
                                                                                            А вот интересно, у hg есть API?
                                                                                            Или имеется в виду python-hglib, который запускает в фоне процесс hg и парсит его текстовый вывод?
                                                                                              • +1
                                                                                                Ну так про что и речь.
                                                                                                Просто интересно, почему cli для hg — это хорошо, а вот для git (для которого помимо прочего есть и libgit2, и jgit) — это уже плохо?
                                                                                                • +2
                                                                                                  Для обоих CLI-only плохо. Другой вопрос, что апстрим Mercurial запилил хотя бы command server, показав тем самым, что они хотя бы подозревают, что Mercurial может использоваться в качестве бэкенда. В отличие от апстрима Git, которые не доводит libgit до юзабельного снаружи состояния принципиально.
                                                                                                  • 0
                                                                                                    А чем же libgit2 (и куча готовых биндингов к нему) плох?
                                                                                                    • +1
                                                                                                      А ничем не плох. Таким должен был быть libgit, но вряд ли уже когда-нибудь будет.
                                                                                              • +2
                                                                                                API официально нет, но на самом деле оно есть. На нём пишутся расширения, и оно на самом деле достаточно стабильно, чтобы его использовать. Просто не забывайте следить за изменениями и прогонять тесты после релиза.

                                                                                                А cli для hg — точно так же плохо, как для git. Те, кому надо написать интеграцию с mercurial просто забивают на совет его не использовать.
                                                                                                • 0
                                                                                                  Правда, я начал писать эту интеграцию когда command server ещё не было. Если бы он уже был, решение было бы, возможно, иным.
                                                                                                  • 0
                                                                                                    API расширений — это одно, API для внешнего использования (из IDE например) — это другое.
                                                                                                    • +1
                                                                                                      Первое прекрасно служит в качестве второго. Замечу, что нужная мне часть почти не менялась с mercurial-1.3. Предположительно и с mercurial-1.2, но в Vim нет sys.stdout.closed, поэтому я всегда получаю AttributeError, когда пытаюсь использовать последний.

                                                                                                      Никто не занимается сломом API «из любви к исскусству», поэтому просто не лезьте в дебри, следите за изменениями и получите нормальную поддержку всех версий, в том числе тех, когда Command Server не существовал. С бонусом в виде отсутствия затрат на IPC или даже жутких затрат на fork/exec.

                                                                                                      На самом деле, расширения ломаются даже чаще — им нужны бо́льшие дебри.
                                                                                                • +1
                                                                                                  git checkout не создаёт ветки. git checkout -b — это всего лишь shortcut на несколько команд.
                                                                                                  git push отправляет/обновляет только состояние веток (указатель) в remote. Удаление указателя — это тоже обновление.
                                                                                                  git status — (из мана) Displays paths that have differences between the index file and the current HEAD commit. Никто и не говорил, что должны показываться изменения в файлах, только изменённые файлы. И это ожидаемо: «Гит, скажи мне какие файлы поменялись»
                                                                                                  git diff — как и ожидается из названия показывает эту самую разницу. «Гит, покажи мне сделанные изменения»

                                                                                                  про push/pull и необязательные аргументы вам ответили. Git децентрализованная система, стало быть remotes у вас может быть несколько. Если вы делаете форк на гитхабе, потом локально редактируете, то наверняка захотите добавить upstream репозиторий для подтягивания изменений к себе. указывать явно репозитоий (origin) — это уже хорошая привычка.

                                                                                                  hg incoming — честно, не работал с mercurial, но полагаю что речь идёт про git fetch.
                                                                                                  Каждый будет хвалить тот инструмент, в котором он лучше разобрался. Это и закономерно, чем лучше разбираешься — тем более успешный опыт, тем круче кажется сам инструмент.

                                                                                                  Единственная проблема (проблема ли?) git — он не поощраяет и не прощает работу по принципу «ну я ща начну работать, мне вот срочно надо, а потом как-нибудь выучу». Разберись, а потом пользуйся и радуйся. Кстати, с линуксами то же самое.

                                                                                                  ПС. Фраза на проекте была: «я уже всё запушил, а потом закоммитал то, что нужно. Забирайте».
                                                                                                  • +2
                                                                                                    Никого не волнует, создаёт ли git checkout -b ветки, вызывает для этого git branch или скачивает vim (с +python), python, pygit2 и libgit2 и по этой цепочке делает своё дело. Вызовом git checkout -b создаются ветки? Да. Значит git checkout создаёт ветки.

                                                                                                    git status не умеет --name-status. В особенности для произвольных ревизий. Хотя, если подумать, то с разницей в информации и формате в выводе, он и не должен уметь. Просто status в git объединяет hg resolve --list и hg status. В mercurial hg status от diff --name-status ушёл недалеко и то только для текущего дерева (для двух ревизий — фактически полные аналоги).

                                                                                                    Обычно у меня одно место куда я делаю push. И другое одно, откуда делаю pull. git — не единственная система, имеющая remotes или аналог. Мне удобно не писать куда я хочу сделать push/pull. С mercurial я не пишу. Что‐то мне подсказывает, что, используй я bazaar, я бы тоже не писал. Почему вообще в git так радикально не любят -o/--option в нескольких ключевых местах? hg push origin --bookmark upstream сразу даёт понятие о том, что и куда вы толкаете. git push origin upstream — нет. Насколько мне известно, mercurial использует только однотипные аргументы без -o/--option: два файла тут встретиться могут, ревизия и файл — никогда.

                                                                                                    hg incoming ничего не загружает в репозиторий. Это не git fetch. При этом коммиты можно просмотреть полностью, вплоть до diff’ов. Конечно, в последнем случае они физически загружаются (и ещё раз при hg pull). Но в репозиторий не попадают. Если я сделаю git fetch, то мне придётся думать, как удалить ненужные изменения. А потом ещё придётся самостоятельно делать fast‐forward для нужных. Может здесь и есть команда вроде hg rollback, но я её не знаю.

                                                                                                    А про линуксы мне говорить не надо. Я просто ничего не уча установил себе Ubuntu. Именно по этому принципу¹. Потом почти так же² пересел на Gentoo, в процессе правда читая handbook.

                                                                                                    Кроме того, как я сказал, замечания не фатальные. За исключение API и hook’ов. А также справки для инопланетян и необходимости знания кишок. Подтянется libgit2, будет гораздо лучше, я и сейчас её начинаю использовать.

                                                                                                    Проблемы из третьего предложения (справка и кишки) меня уже слабо волнуют. Но это причина, по которой я не буду советовать git ни одному из новичков. Тем более, что git позволяет новичку отстрелить себе ногу с максимальной эффективностью: удалить всё и у себя, и на том конце. Неизменяемая история после git меня уже слегка раздражает, но 1. над этим работают и 2. это давало уверенность в том, что фатально я ничего не напорчу. (Давало: сейчас такую уверенность даёт опыт работы с обеими системами.)

                                                                                                    ¹ В Windows по совершенно непонятным причинам через несколько дней после установки начинал тормозить интернет. В Ubuntu он не тормозил. Вторая часть мотивации: linux — это не так как у всех, а потому круто (как несложно догадаться, решение принималось ещё в школе). Ни одного linux‐гуру вокруг не было, только такие же разговоры от других школьников, которые его где‐то видели. «Совершенно непонятные причины» также замечательно характеризуют уровень компьютерного знания.
                                                                                                    ² Только на этот раз было не «надо», а «интересно». И вторая часть ещё никуда не делась. Ну и проблемы от неопытности я себе тоже создал. Не такие большие, как были в Windows, и не причина перейти на что‐то радикально новое, но тоже не сахар.
                                                                                                    • +1
                                                                                                      git checkout -b — это всего лишь shortcut на несколько команд
                                                                                                      Это Же Совсем Другое Дело!
                                                                                                      git push отправляет/обновляет только состояние веток (указатель) в remote
                                                                                                      Ну конечно. А git send-pack, значит, юзер отдельно командует.
                                                                                                      указывать явно репозитоий (origin) — это уже хорошая привычка.
                                                                                                      Зачем указывать, если можно не указывать?
                                                                                                      честно, не работал с mercurial, но полагаю что речь идёт про git fetch
                                                                                                      Полагаете абсолютно неверно. Речь о команде, бледным подобием которой является git request-pull. Забавно, что «не читал, но осуждаю» от одептов git даже не воспринимается
                                                                                                      как что-то из ряда вон выходящее.
                                                                                                      Каждый будет хвалить тот инструмент, в котором он лучше разобрался.
                                                                                                      Как же быть, если я разобрался в обоих примерно одинаково (неплохо)?
                                                                                                      git — он не поощраяет и не прощает работу по принципу «ну я ща начну работать, мне вот срочно надо, а потом как-нибудь выучу»
                                                                                                      Вы уверены, что это именно вы используете git? Точно не наоборот?
                                                                                                • 0
                                                                                                  Мода/популярность

                                                                                                  Между прочим при прочих равных это не такой плохой аргумент в IT
                                                                                                  • +8
                                                                                                    Не плохой. Будь у git ещё, к примеру, нормальный UI, а не просто внутренности наружу — этот аргумент можно было бы даже воспринимать всерьёз.
                                                                                                    • –15
                                                                                                      О теперь про UI, удивительно как похожи все аргументы приверженцев Hg :)
                                                                                                      Git не нужен UI как таковой, потому что все делается достаточно просто, быстро, удобно и безопасно из консоли.
                                                                                                      UI может требоваться в двух случаях merge/diff — для этого есть миллион утиит которые прекрасно подключаются к Git и Mercurial впрочем тоже.
                                                                                                      И чтобы посмотреть/поискать по дереву, с этим отлично справляется gitk (хотя есть еще десяток проектов разнйо степени стрёмности)
                                                                                                      • +6
                                                                                                        как похожи все аргументы приверженцев Hg
                                                                                                        Как будто это что-то плохое.
                                                                                                        все делается достаточно просто, быстро, удобно и безопасно из консоли
                                                                                                        Bullsh эээ… bingo!
                                                                                                        • +1
                                                                                                          С каких это пор «command-line interface» перестал быть «user interface»? Спрашивали же не «graphical user interface» (aka GUI). Хотя претензии мне здесь не совсем понятны: CLI, конечно, местами нелогичен, но не настолько, чтобы это было проблемой. Если бы была нормальная документация конечно.

                                                                                                          А «внутренности наружу» — скорее всего имеются ввиду огромное число команд вроде «git unpack-file», предназначенных исключительно для использования их в скриптах.
                                                                                                          • +8
                                                                                                            Имеется в виду то, что потроха Git (object, ref, blob, tree) торчат отовсюду. Даже прямо с порога:
                                                                                                            %  git
                                                                                                            usage: git [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
                                                                                                            …skip…
                                                                                                               push       Update remote refs along with associated objects
                                                                                                            …skip…
                                                                                                            «Это математик, Ватсон».
                                                                                                          • +1
                                                                                                            Мы на работе уже сколько лет как работаем на гите и есть два человека в команде, которые терпеть не могут вид консоли, не хотят запоминать команды и вообще.
                                                                                                            Удивительно, как похожи контр-аргументы гитфанов :).
                                                                                                            • +1
                                                                                                              Пользователи windows? Я бы тоже не смог терпеть вид консоли.
                                                                                                              • +2
                                                                                                                Ага, лично я плохо понимаю, почему они не могут перейти на линупс как уже давно сделала вся остальная команда, но это их личное дело.
                                                                                                                Ну и вот, собсна, проблема нормального GUI не решена. Ближе всего SmartGit, но он платный, а никто им покупать его не стремится. Бесплатных GUI, хотя бы близко приближающихся к Mac'овскому SourceTree походу в природе не существует. (Тут должна быть вставлена картинка «WHY?»)
                                                                                                                • 0
                                                                                                                  Раз уж пошло обсуждение гитовских GUI, а вы пользуетесь линуксом, то чем вы в нём пользуетесь?
                                                                                                                  Под маком есть прекрасный Tower (если денег не жалко) и чуть менее прекрасный SourceTree. А вот под линуксом меня от имеющихся интерфейсов аж передёргивает.
                                                                                                                  • 0
                                                                                                                    Лично я на коммерческих проектах пользуюсь то gitg то rabbitvcs. Убоги оба, но как-то юзабельны. На бесплатных — SmartGit — он чуть лучше, но до SourceTree все равно не дотягивает. Ну и лично у меня нет проблем с консолью :).
                                                                                                                    • +2
                                                                                                                      Я, если честно, не представляю человека, который пользуется линуксом и обламывается работать с git через консоль :-)
                                                                                                                      • +1
                                                                                                                        Как бы там ни было, я тоже человек и хочу sourcetree под линукс.
                                                                                                                        • 0
                                                                                                                          Спору нет, смотреть дерево удобнее в красивом gui. Меня спасают две вещи:
                                                                                                                          cit config alias.tree = log --graph --pretty=format:'%C(bold yellow)%h%Creset %s %C(bold blue) %an%C(bold cyan)%d %C(bold black)-> %cr%Cgreen'
                                                                                                                          и отсутствие необходимость смотреть дерево. :-)
                                                                                                                          • 0
                                                                                                                            Ну так в гуях много чего приятно делать — например выбирать файлы для коммита, ревертить случайно поменянные файлы… Не поймите меня неправильно — это умеют все клиенты. Но SourceTree пользоваться удобно, а другими — нет.
                                                                                                                            • 0
                                                                                                                              Фиговатый у вас пример-то :)

                                                                                                                              Одна длинная строка — и все отображение развалилось…
                                                                                                                              • +1
                                                                                                                                У меня эран широкий и хук на длину сообщения. Но вообще да, надо поправить как-нибудь.
                                                                                                                                • 0
                                                                                                                                  А еще видите там вон такую горизонтальную псевдографическую полоску? Она вообще к чему? Это и без разрыва-то небось неочевидно… Ну и главное — такую сложную алхимию я бы не написал — а в гуях это сделано за меня…
                                                                                                                                  • 0
                                                                                                                                    А еще вот вообще трэш и угар:
                                                                                                                                    • 0
                                                                                                                                      Да, похоже вам мой конфиг не подойдет :)
                                                                                                                                      Но мне как-то хватало, уж не знаю почему. Возможно из-за того, что у меня нет таких ветвлений. Или я не говорю --all. :-)
                                                                                                                                      • 0
                                                                                                                                        Да тут на самом деле интересная штука — один из коллег любит делать пространные комментарии к коммитам, за что его уж совсем никак винить нельзя, кроме того так получалось, что на последнем рассмотренном куске не было еще фичебранчей — он тупо раз за разом коммитил все в одну ветку. Причем коммитил он чаще других — у него были десятки относительно небольших задач. Вот и получается — одна ветка только изкоммитов, все остальные — просто прямые (их не так уж много — мастер плюс еще какие-то побочные).
                                                                                                                                        Но вот оказывается, что нельзя достаточно полно посмотреть на обычном экране дерево в хоть сколько-то сравнимой с гуи форме.
                                                                                                                                        Вот как оно выглядит в gitg:

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

                                                                                                                                        насчет all — тупо скопировал команду из вашего альяса.
                                                                                                                                        • 0
                                                                                                                                          Я имел в виду, что либо у вас много длинных параллельных веток в локальном репозитории, либо вы добавили git tree --all. С ветками прояснилось, так что извините.

                                                                                                                                          Я не спорю, что красивый gui полезен для просмотра и повседневной работы разработчика/менеджера, но для меня он не очень важен, а вот многие вещи, которые есть в командной строке (filter-branch например) я использую относительно часто. Так что так выходит, что мне GUI не нужен.
                                                                                                                                          • 0
                                                                                                                                            Но вот первый скрин уже был при фичебранчах — но все равно разработчиков несколько и у каждого свой фичебранч.
                                                                                                                                            • 0
                                                                                                                                              запустите gitg --all (если он такой же, как gitk), может ветки спрятались
                                                                                                                                          • +1
                                                                                                                                            один из коллег любит делать пространные комментарии к коммитам, за что его уж совсем никак винить нельзя
                                                                                                                                            Посоветуйте своему коллеге в первой строке комментария писать очень коротко о чём этот коммит вообще, а пространный комментарий начинать со второй строки.
                                                                                                                                            • 0
                                                                                                                                              Ну не всех просто взять и переучить, к сожалению.
                                                                                                                                              • 0
                                                                                                                                                Принято с третьей. А вторая строка тогда должна быть пустой.

                                                                                                                                                Ну и вполне можно не вылезать за 120 символов на строку, например. Хотя для первой строки принято не более 49 символов писать.
                                                                                                                                  • 0
                                                                                                                                    Напишите в atlassian. :-)
                                                                                                                                  • 0
                                                                                                                                    Ну, вот я такой, например. Собственно, под линуксом я и пользуюсь консолью (и немного gitg), но после работы с хорошим GUI обратно в консоль не хочется. Возможно, вы просто не пробовали.
                                                                                                                          • +4
                                                                                                                            Именно консольный UI у git ужасен.