Показаны сообщения с ярлыком контроль версий. Показать все сообщения
Показаны сообщения с ярлыком контроль версий. Показать все сообщения

2015-01-29

Редактирование истории в git

Этот пост что-то вроде Advanced mercurial но для git. Рано или поздно все сталкиваются с двумя проблемами: у меня куда-то пропали коммиты и я запушил не туда. Во втором случае есть простой случай, когда никто не успел спулить и когда неправильный кормит у всей команды. Все опасаются применять команды с ключами --hard и --force, но это совсем не страшно, если понимаешь, что все обратимо и ты понимаешь чего хочешь достичь этими командами.

Концептуальное отличие от mercurial: в git коммит обязательно должен принадлежать бранчу (без бранча коммит становится detached head), а бранч может быть только один. В mercurial ветки могут быть анонимными, более того может быть несколько веток с одним именем. Чтобы коммит стал виден, должен существовать путь до него из какого-либо бранча. То самое отличие кроны и корневой системы (почему-то именно такая аналогия приходит в голову). В mercurial чтобы коммит был доступен должен существовать путь до него от 0-го коммита, а имена и прочее это мелочи. Соответственно и можно четко проследить начало и конец любой ветви, в отличии от git, где коммиты эфемерные дельты болтающиеся по узлам DAG. Хорошо это или плохо - зависит от, но в централизованной разработке мне больше импонирует mercurial с долгоживущими и вполне материальными ветвями, хотя это не значит что там нельзя редактировать историю - дельта алгебра везде одна. Git же заточен именно под распределенность и множество центральных узлов - помимо ветки foo-branch, обязательно есть origin/foo-branch или another/foo-branch с четким разделением откуда приехал коммит - в mercurial же именование веток сквозное по всем репозиториям, если стянули изменения то они уже ваши и отделить где чьё не получится.

Дальше пойдет описание трех сценариев в демке с github. Демка пошагово демонстрирует работу Алисы и Боба с общим репозиторием. Запускаем, читаем комментарии, жмем батон. Демка должна работать на unix-like системах где есть bash. Кстати, ознакомьтесь со статьей tonsky - прольет свет на внутренности git.

2014-10-25

Про Git и Mercurial

Моя борьба с git длится уже пятый год. Большую часть времени я его просто игнорировал. Последние пол года начал активно работать. Вообще, прекрасная иллюстрация импринтинга: первой системой была svn, а затем произошел переезд на идейного наследника mercurial - после чего git кажется технологией пришельцев. Первые попытки были вынужденными: нужно было попатчить драйвер couchdb для диплома, заслать пару пуллреквестов - каждый раз приходилось открывать шпаргалку и копипастить команды в консоль. Не помогла даже книга Pro Git, пол года назад пролистал еще раз - пока читаешь - все понятно, но повторить не смог бы.

Есть базовая идея, которая лежит в основе, без понимания которой все остальное не имеет смысла. В идеологии git есть один мастер бранч, все остальные - временные недоразумения, которые либо вливаются в главный, либо удаляются целиком, безымянные бранчи не могут существовать (хотя конечно есть reflog). В mercurial, как наследнике svn, бранчи - полноценные долгоживущие сущности, которие живут и после слияния. Поэтому граф git выглядит как корень дерева, а mercurial - как крона. Поэтому в git сложно взглянув на граф определить в каком бранче был сделан коммит, как он попал в текущую ветку и если ли эти изменения в ней вообще.

Другая идея важная для понимания - в git, пока не был сделан merge или rebase, визуально четко разделяются чужие ветки и твои (branch-name vs origin/branch-name) поэтому fetch совершенно безопасен, поэтому push --force деструктивнее, но крайне удобен если понимаешь, что ты делаешь, в mercurial как только коммит попал к тебе, он становится твоим сразу же, поэтому hg strip приходится делать каждому разработчику. Интересно, что mercurial органично впитывает лучшие идеи git: strip, rebase, rebase -i, stash, backup copy при деструктивных операциях - обратного дрейфа не заметно.

После того как освоил инструмент, обязательно начинаешь затачивать под свой workflow. Недавно, задействовав все свое bash-fu, родил монструозный алиас, чтобы понять в каком же бранче находится закрытый тикет (на самом деле главное - смерджен/не смерджен). Делать
git log --grep
git branch --contain
быстро надоело, поэтому легко заменить на
git gb #ISSUE-1234
при помощи
gb = !git --no-pager log --format=\"%H\" --decorate --all --grep=$1 | xargs -I {} sh -c \"git branch --contains {}| paste -sd ',' - && git --no-pager log --oneline --decorate {}^..{}\"

ЗЫ

Великолепный курс от codeschool
Интересная статья от tonsky
Мой .gitconfig

2013-11-17

Статистика владения кодом

Давно была идея визуализировать impact человека на продукт. Мощные визуализаторы типа codeswarm (активность и совместная работа) и gource (процесс работы) показывали не совсем то, что хотелось - текущее состояние. При помощи hg log -u username -p | grep "^+" | wc -l  можно посчитать добавления и удаления, но опять же в динамике не увидеть. Хотелось увидеть как росла доля пришедшего человека, насколько быстро она затухала с уходом и какое наследие осталось.

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

Полез курить Mercurial API, заодно накидал ссылок в вики-статью и скачал бесплатный PyCharm. На проект не тянет, поэтому сохранил в gist (derived work требует назначить лицензию GPL). Программа последовательно в каждой ревизии аннотирует текстовые файлы и считает, количество строк для каждого коммитера. Тестировал на маленьком репозитории, и не заметил нескольких улучшений. Пока работал на нашем основном - успел внести несколько правок.

Процесс запуска

1. Запускаем первый раз python participation.py - у нас появится шаблон конфига
2. Отключаем подсчет calculate=false и прописываем репозиторий repo=~/path/to/repo
3. Запускам еще раз и получаем на выходе пользователей
4. Прописываем им алиасы в разделе [Alias] - часто один пользователь попадает под разными именами, а мы не хотим портить статистику
5. Наконец включаем подсчет и запускаем
6. Ждем результат, открываем в Excel и рисуем график

Статистика

На серьезных репозиториях работает ОЧЕНЬ долго.
Проанализировано 164.564.370 строк в 1135 коммитах (только ветка default) за 8 часов на Atom N550, потребление памяти в пике 75мб, диск был SSD, но IO был неизмеримо мал.

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

2013-10-28

Hgext.markdown for Windows

Python eggs

Main problem with this extension on Windows OS was external dependency Markdown and unlike on Ubuntu you cannot execute apt-get python-markdown in your shell. TortoiseHG has bundled python and you cannot simply add egg inside dist-packages. I look at source and find out, that original way for Windows user was uncomment and edit line sys.path.append("c:/python27/Lib/site-packages/markdown-2.2.0-py2.7.egg") but nothing was in documentation about it.

So I decide to rewrite this mechanism in fallback style: first we try to load markdown package and if failed, lookup using path from config (python can transparently lookup files in zip archives):
try:
    from markdown import Markdown 

except ImportError:
    dir = os.path.dirname(os.path.realpath(__file__))
    md = ui.config("web", "markdown.egg", "Markdown-2.3.1")
    ui.debug("Markdown not found search for egg in local dir %s for %s.zip\n" % (dir, md))
    sys.path.append(os.path.join(dir, "%s.zip\%s" % (md, md)))
    try:
        from markdown import Markdown
    except ImportError:
        ui.error("Unable to locate markdown in path %s" % sys.path)
global Markdown


this is final version corrected by Chris Eldredge. Now you can download markdown package and put it in extension folder.

Relative paths

Rendering comparison
BitBucket vs hgext.markdown

Second big improvement is relative paths in markdown files. We massively use crossreferences in the docs we store in our repository, and quickly find out some problems with relative links in which used parent (..) directory and hash (#). I fixed this and update demo page with picture and relative navigation. Unfortunately BitBucket incorrectly render this markdown because of the bug.

Preview

Also now you can navigate to preview of uncommited files with one click in menu. Not a big deal but usability improvement.

Summary

Chris already merged this patches to his repo. So if you want to see beautiful doc instead of raw markdown, even on Windows it is good time to install or update extension.

2013-08-31

Advanced Mercurial

Очень много людей вокруг, активно используют Mercurial, но при этом ограничиваются базовыми вещами типа push/pull/commit/update да и те выполняются через tortoisehg. Я в начале августа выступал с рассказом о чрезвычайно полезных фичах, про которые, когда начал пользоваться, думаешь: как же я жил без этого раньше. Слайды оформлю в виде шпаргалки.

2013-06-15

Markdown в репозитории и оформление веб-интерфейса Mercurial

В мае появилась задача подумать над хранением документации. Где мы только не пытались хранить документацию: и в Dropbox, и в Google Docs. Последнее, где мы остановились, это - Confluence. Совершенно наркоманская система редактирования, потребовалось ставить отдельный плагин, чтобы можно было редактировать чистый HTML. Главная проблема всех этих подходов - рассинхронизация. Документация не соответсвует коду. На втором месте - невозможность автоматической генерации документации в процессе сборки проекта. От системы документации не требуется многого: вставка изображений и кросс-ссылки - более чем достаточно. 

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

Mercurial + Markdown

Хотя README встречались и раньше, а wiki хранилась в репозитории еще на Google Code, но именно с приходом GitHub README.md стало обязательным требованием. Краткое описание проекта, в wiki-стайл разметке markdown. После знакомства с возможностями этой системы на github.io любые сомнения в возможностях разметки пропадают. Приятным бонусом является плагин для IntelliJ IDEA.

Очень хотелось получить это на локальном репозитории. Поиск вывел на относительно молодой проект (не первый раз ищу) hgext.markdown от Chris Eldredge. Плагин для mercurial, покрывающий требования к встроенной системе документации на 100%. Когда я его поставил, выснилось, что разметка применяется только к readme, плагин не умеет работать с кодировками (пришлось вспомнить эпичнейший доклад про кодировки), с картинками (необходимое требование), ну и несколько проблем шаблонами вообще и с разметкой в частности.

Pull request or GTFO

В общем все это я пофиксил и заслал Крису толстенный пуллреквест, пару дней назад он принял его. Линуксоиды могут спокойно качать плагин, виндузятники в пролёте, поскольку требуется библиотека а у mercurial встроенный python, и как подсунуть одно в другое, еще не разбирался. Как выкурю - зашлю еще один пуллреквест с инструкцией.

Пока ждал - пошел на IRC канал, чтобы найти кого-нибудь с правами на запись в wiki-страницу со списокм плагинов. Оказалось, что особых прав не нужно и достаточно зарегистрироваться (порадовала каптча, которая требует ответить на вопрос про команды mercurial). Вооружившись руководством, создал по фэн-шую страничку плагина.

Темы для mercurial

В процессе ковыряния плагина набрел на встроенную систему шаблонов и тем для hgweb. Внутри крайне разухабистый движок, с доступом ко внутреннему API, позволяющий изобразить решительно всё. Заскриншотил каждую тему и добавил на страницу со списком, чтобы было понятно как каждая выглядит. Кстати, сам mercurial размазан по файловой системе: бинарники лежат в /usr/share/mercurial/templates, а основной код в /usr/lib/python2.7/dist-packages/mercurial.

Пять стандартных тем и Markdown, идущий в комплекте с плагином

2013-05-07

Топологическая сортировка репозитория

Abstract

В систему контроля версий mercurial версии 2.6 [1] был добавлен новый алгоритм сортировки closesort в команду convert, позволяющий оптимизировать расположение коммитов, в которых закрываются ветки. В отличие от алгоритма datesort, который might well increase the size of the destination repo by 10-20 times [2], closesort незначительно меняет размер репозитория. Данный алгоритм может быть интересен для workflow в которых активно ведется работа с ветвями, причем все открытые ветви закрываются.

Rationale

Fig. 1 Визуальный мусор
При активной работе с ветками рано или поздно может возникнуть ситуация, когда необходимо массово закрыть устаревшие ветви. Множество очень старых ветвей приводит к тому, что граф заполняется визуальным мусором. На Fig. 1 изображен типичный пример такой массовой чистки устаревших ветвей, на 1 живую ветвь приходится 15 мусорных. 

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

Methods

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

Попытка 1

Поиск готовых или похожих решений натолкнул на обсуждение [3] проблемы оптимизации чрезвычайно больших репозиториев, вылившихся в итоге в расширение shrink-revlog [4]. Данное расширение как раз занималось топологической сортировкой и достаточно было написать собственную функцию сортировки к двум имеющимся. Алгоритм был написан, при этом потребовалось прокинуть внутрь функции сортировки объект репозитория.

Но первый же запуск на реальном репозитории привел к ошибке выхода за пределы массива. Выяснилось, что в репозитории используется два файла changelog и manifest [5]. В первом хранится информация о коммитах, а во втором информация о файлах [6], при этом, если в коммите не было изменений в файлах, то появится расхождение в нумерации между этими двумя файлами. Сортировка же changelog была запрещена, поскольку это повредит репозиторий.

Попытка 2

В качестве "Плана Б" можно было встроиться в стандартное расширение convert. В нем уже присутствовали branchsort, datesort и sourcesort. Необходимый алгоритм отличался от sourcesort одним условием:
        def makesourcesorter():
            """Source specific sort."""
            keyfn = lambda n: self.commitcache[n].sortkey
            def picknext(nodes):
                return sorted(nodes, key=keyfn)[0]
            return picknext

        def makeclosesorter():
            """Close order sort."""
            keyfn = lambda n: ('close' not in self.commitcache[n].extra,
                               self.commitcache[n].sortkey)
            def picknext(nodes):
                return sorted(nodes, key=keyfn)[0]
            return picknext 

И успешно решал поставленную задачу.

Pull request

Я сразу пошел в IRC разработчиков с целью обсудить данное решение, но особого интереса там не проявили и отправили в мейллист. После этого, Kevin предложил попробовать branchsort [7]. Данный алгоритм упорядочивал только самые простые случаи, но не справлялся со сложными. На IRC мне посоветовали засылать патч, раз никто не возразил. Патч был заслан и запушен Bryan [8]. В этот раз на всё-про-всё, от идеи до аппрува, ушел всего лишь месяц, а не пол года, как в прошлый раз [9].

Results

Максимальная ширина

На реальном рабочем репозитории с 5528 коммитов, максимальная ширина графа составляла 60 параллельных веток. После конвертации, ширина максимальная ширина репозитория уменьшилась до 26.

Средняя ширина

Подсчет средней ширины графа производился командой hg log -G | awk '/changeset/ {cnt++; width += (index($0,"changeset")-2)/2} END {print width / cnt}'. Данная подсчет не учитывает некоторых особых случаев, но подойдет для примерной оценки. До конвертации 21.16 и после 6.08.

Discussion

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

2013-04-24

Бизнес-логика в базе


Типичный опердень
Выборка 41.5%
Фильтрация 5.8%
Обработка 13.4%
Аналитика 17.7%
Пост-обработка 21.6%
Комментарии 0%

Или почему это зло

Если бизнес-логика в базе

Первая по важности - это богатые возможности СУБД: умный оптимизатор, который может работать с индексами и собирать статистику, что драматически сокращает объем считываемых данных и памяти; под рукой дешевые джойны по внешним ключам; агрегатные и аналитические функции; группировка и сортировка; фильтрация.

Второе - возможность легко и непринужденно использовать результат такого запроса в качестве источника данных.

Если бизнес-логика в приложении

Нет размазывание бизнес-логики между приложением, и базой. Что же такого на стороне приложения?
1. Не нужно вырезать и вставлять куски кода, чтобы их прогнать в изолированном окружении
2. Легкая отладка - можно остановиться в любом месте обрабоки и посмотреть на данные
3. Версионирование - кто вообще версионирует код, в базе?
4. Легковесность базы - нет логики, достаточно простого SQL, нет требований к базе, можно на коленке разворачивать схему любой версии на любой пользовательской машине. Когда база всего-лишь тупое хранилище данных, нам для работы достаточно командной строки, в идеале поля для выполнения кода с подсветкой синтаксиса. Сам билд становится переносимым, позволяя развернуть рабочее приложение на чистой машине.
5. Декларативный подход против императивно-функционального - СУБД сама выбирает оптимальный порадок выборки данных, зато во втором варианте процесс обработки можно "читать"
6. Комментарии - их нет. Ноль целых ноль десятых процента. Какой смысл их писать, если один кусочек логики размазывается по всему запросу? В случае пошаговой обработки можно пояснять что мы делаем на каждом этапе и почему.
7. Пошаговость процесса и функциональный стиль написания - позволяет писать тесты. Тестирование кода, работающего с базой - это уже джедайские практики. Я ни разу не видел тестов внутри базы, покрывающих пакеты, хотя инструменты имеются.
8. Возможность написать высокоуровневый DSL, который будет адаптирован к бизнес-домену, что позволит транслировать код в SQL с жутким мультипликатором. На эту тему у ребе metaclass можно прочитать много интересного.

Минусы хранения логики в приложении

1. ORM, которые строят сложные и, порой, неоптимальные запросы. А еще, многие из них не умею работать с курсорами, что усложнает написание кода в функциональном стиле с минимумом побочных эффектов. Претензий много.
2. Резко вырастает объем данных, которые нужно загрузить для выборки, одна из причин, по которой аппликейшен сервер держат поближе к базе данных (в сравнении с расстоянием до клиента).

Итог

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

P.S. Пояснения к легенде

Выборка - это Select и Join,
Фильтрация - Where и Having,
Обработка - различные функции, рассчеты, кейсы,
Аналитика - агрегатные и аналитические функции,
Пост-обработка - захардкоженные константы, сортировка и группировка.

2013-04-21

Версионирование схемы БД

1 часть. Ораклява

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

2013-04-20

Липосакция репозитория

Сборка HelloWorld на Maven

Предыстория

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

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

Хирургическое вмешательство

1 Этап. Определение кандидатов на отстрел

Такие файлы должны удовлетворять двум простым правилам:
  • это должен быть бинарный файл
  • он должен быть удален на момент поиска
Бинарный файл можно опредлить по расширению и отсутствию изменений в этом файле.

2. Этап. Составление расстрельного списка

Используем стиль для вывода списка файлов:

#Для списка измененных
#changeset = "{file_mods}"
#Для списка удаленных
changeset = "{file_dels}"

file_mod  = "{file_mod}\n"
file_del  = "{file_del}\n"

Дальше составляем два списка командами hg log --style style_del | sort -u и hg log --style style_mod | sort -u. В первом приближении нам нужно из списка удаленных исключить все элменты которые встречаются в списке измененных. Вычесть файлы можно несколькими способами. Дальше вычитаем при помощи пайпов:
comm -2 -3 \
<(hg log --style ~/path/to/style_del | sort -u) \
<(hg log --style ~/path/to/style_mod | sort -u).
Для экспериментов можно передавать диапазон ревизий -r 0:100 чтобы оптимизировать время.

Полученный список нужно будет просмотреть глазами и составить список расширений для удаления. Список расширений можно посмотреть прогнав через | grep -oE "\.\w*$" | sort -u. В нашем случае он получился таким: deb, gz, jar, mp3, so, wav.

Прогоняем через sed, выводя только файлы, совпавшие по расширению, формируя filemap для hg convert. Итоговая строка фильтрации выглядит так:
comm -2 -3 \
<(hg log --style ~/path/to/style_del | sort -u) \

<(hg log --style ~/path/to/style_mod | sort -u) \
| sed -n 's/\(.*\)\(deb\|gz\|jar\|mp3\|so\|wav\)/exclude "&"/p' \
> filemap

3 Этап. Зачистка

Получившийся filemap скармливаем в hg convert --filemap ~/path/to/filemap ~/repo ~/repo_clean. На выходе получаем переродившийся репозиторий без шлака и мусора.

Дисклеймер

Здесь описана операция, успешно проведенная нами год назад. За это вресмя ни разу не понадобилось доступиться к удаленным файлам. На данный момент при 5к коммитах, репозиторий занимает 500Мб. При этом часть бинарных ресурсов всё еще жива и используется.

Фильтрация идет в основном средствами линуксовых команд. Хотя mercurial предоставляет прекрасный API для доступа к репозиториям, которым сам же и пользуется, на котором можно было бы написать понятную фильтрацию без подобных майндфаков: /\(.*\)\( и приседания с ключами команд, на тот момент с этим API я был незнаком, а сейчас просто воспроизвожу свои записки для сохранения в назидание потомкам.

2013-04-15

Дао версионности

Предыстория

На третьем курсе зав.каф. Захаров Ю.Н. отправил меня помогать старшекурснику в написании визуализатора треков частиц. С меня было программирование, со старшекурсника математическое обеспечение. В тот момент у меня откуда-то появилось знание (возможно с хабра), что Ъ-пацаны в командной работе юзают контроль версий. State-of-the-art на тот момент был subversion, mercurial с git, хоть и родились но пугали кучей проблем (возможно отталкивало отсутствие опыта). Я заинитил репозиторий, выдернул из меркуриала консольные утилиты и начал его юзать. Использовал в одиночку, хоть и не долго.

2012-11-05

Pro Git

На хабре выложили русский перевод сабжа. Ну и раз появилась такая возможность захотелось выяснить, наконец, кто круче: git или mercurial.

2012-10-13

Культурные слои в коде


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

2012-03-15

Git-flow

Тот самый git-flow про который упоминали ребята из 2GIS на Кодефесте
Не вижу препятствий для использования в Mercurial. Сейчас мы используем похожую модель, только ветки у нас default и feature-*. До этой статьи было совершенно непонятно как выкатывать релизы и фиксить в них баги. Сейчас все встало на свои места.