Статьи

Как Google делает обзор кода

Об Мондриане много говорили на протяжении многих лет. Гвидо Ван Россум, который руководил разработкой, рассказал об этом в 2006 году и о том, как он это сделал:

Гвидо тогда был Гуглером, но теперь он работает на DropBox. Очень хороший парень.

Это может быть устаревшим, поскольку у меня есть контекст только до конца 2008 года, но по большей части они только проверяют коммиты, которые происходят на Транке. Помимо Android, Google занимается разработкой магистральных систем (TBD) везде. Они сделали процесс обзора максимально легким, сделав обзоры обычными и непрерывными. По мнению Google, списки изменений (CL) от Perforce либо не рассматривались, либо не проверялись. Мондриан следит за ними. Релизы как таковые не имеют позднего цикла обзоров. Действительно, он чувствует, что «Обзор» — это не сущность в Мондриане, а еще один артефакт коммита (CL). Поскольку он не является сущностью сам по себе, существует соотношение 1: 1 отзывов к CL. Сейчас я работаю над этим, но списки изменений — единственное существительное, существующее здесь.

На практике:

  1. Более одного человека могут погрузиться в обзор
  2. Некоторые люди будут более склонны, чем другие, оценивать работу других
  3. Отзывы происходят в течение нескольких минут или часов после завершения кода
  4. Как разработчик, вам, естественно, будут предложены обзоры вещей в области вашего проекта (или библиотеки / фреймворка).
  5. .. Но вы также можете рассмотреть что-то полностью вне вашей назначенной области

Таким образом, для Google «Непрерывный обзор» — это просто быстрое получение незавершенных ожидающих коммитов . Под «ожиданием» я подразумеваю, что коммиты еще не попали в ствол. Действительно, пока они не «пройдут» рецензию, они не собираются. Ожидающие коммиты Perforce могут иметь уникальный номер и могут быть извлечены с рабочей станции разработчика еще до того, как они будут допущены к транку для будущих поколений (после успешного просмотра). Гвидо пришлось сделать много инструментов, чтобы сделать это гладким, хотя.

Внутренний Mondrian от Google вдохновил двух эквивалентов с открытым исходным кодом

Другие инструменты

Не имеет отношения к Мондриану:

  • Atlassian’s Crucible — это большой коммерческий успех (Git, Mercurial, Subversion и Perforce)
  • ReviewBoard (с открытым исходным кодом и для Subversion, Git, Mercurial, Bazaar, Perforce, ClearCase & Plastic)
  • Phabricator (с открытым исходным кодом и для Git, Mercurial и Subversion)
  • Collaborator «(коммерческий для — Git, Mercurial, Subversion, ClearCase, Perforce и TFS)

Все это можно установить. Есть порталы, такие как Github, которые имеют встроенный механизм обзора, и я не буду вдаваться в них.

Предприятия кроме Google

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

Atlassian’s Crucible — это устанавливаемый инструмент, который работает следующим образом: назначаемые обзоры, которые могут происходить в любое время и обычно представляют собой совокупность коммитов. Тигель (на котором я собираюсь сосредоточиться в оставшейся части этой статьи) может проверять коммиты на любом количестве веток и пытаться понять происхождение.

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

Проблема — не зная, что вы уже просмотрели что-то

Мы собираемся погрузиться в рабочие примеры здесь, особенно для Crucible, так как это Горилла в комнате.

Установите свой собственный Crucible (30-дневная демоверсия загружается) и запустите его.

Тигель с Subversion

Сделайте репозиторий Subversion с помощью этого скрипта (create_repo.sh):

#!/bin/sh
svnadmin create $2 
perl -p -i -e "s/# anon-access/anon-access/g" $2/conf/svnserve.conf 
perl -p -i -e "s/# auth-access/auth-access/g" $2/conf/svnserve.conf 
perl -p -i -e "s/# password-db/password-db/g" $2/conf/svnserve.conf 
perl -p -i -e "s/# harry = harryssecret/harry = harry/g" $2/conf/passwd 
svn mkdir file:///path/to/root/directory/$2/trunk -m "create trunk"
svn mkdir file://$1/$2/branches -m "create branches"

Используйте это так, прежде чем запускать Svn Server:

./create_repo.sh /path/to/root/directory repo_name
sudo svnserve -d --foreground -r /path/to/root/directory

В консоли администратора Crucible добавьте репозиторий для svn: //127.0.0.1/repo_name

Запустите этот скрипт оболочки (svn_test.sh), чтобы заполнить каталог:

#!/bin/sh
mkdir -p svn_workingcopy
rm -rf svn_workingcopy/*
rm -rf svn_workingcopy/.svn
svn --username harry --password harry co $1 svn_workingcopy 
cd svn_workingcopy/trunk
echo "** Create trunk version of file"
echo "Efficiently unleash cross-media information without\nimmediate value. Dramatically\nmaintain clicks-and-mortar solutions without\nfunctional aspects." > file.txt
svn add file.txt
svn --username harry --password harry commit -m "initial version on trunk" 
# svn --username harry --password harry copy $1/trunk $1/branches/br1 -m "branch br1 from trunk w/o mods" 
echo "** Create Branch .."
svn --username harry --password harry copy $1/trunk ../branches/br1 
if [ "$2" = "mod" ]; then
  echo "** .. With small Modification during branching"
  perl -p -i -e "s/aspects/bits and pieces/g" ../branches/br1/file.txt
fi
svn --username harry --password harry commit .. -m "branch br1 from trunk with one mod" 
echo "** Modify trunk version of file"
perl -p -i -e "s/cross-media/sexed-up/g" file.txt
perl -p -i -e "s/clicks-and-mortar/empirical/g" file.txt
svn --username harry --password harry commit -m "follow-up change on trunk" 
cd ../branches
svn up --username harry --password harry
cd br1
echo "** Modify branch version of file"
perl -p -i -e "s/cross-media/socially-awkward/g" file.txt
perl -p -i -e "s/immediate/any/g" file.txt
svn --username harry --password harry commit -m "divergent and potentially clashing modification on the branch"
svn up .. 
echo "** Merge trunk to branch at HEAD, preserving 'any' from the br1 ancestor, taking 'empirical' from the trunk, and choosing neither ancestor with 'tie-dyed'"
svn --username harry --password harry merge --accept=postpone $1/trunk . 
echo "Efficiently unleash tie-dyed information without\nany value. Dramatically\nmaintain empirical solutions without\nfunctional aspects." > file.txt
if [ "$2" = "mod" ]; then
  perl -p -i -e "s/aspects/bits and pieces/g" file.txt
fi
svn --username harry --password harry resolve --accept=working file.txt
svn --username harry --password harry commit -m "merge from trunk to branch, with resolved conflict (and an auto-merge)" 

Затем зайдите в веб-интерфейс и взгляните на «График коммитов» для репо:

Коммит, который я выделил (# 4) создал ветку. Обратите внимание, что в прошлой статье был обнаружен дефект Subversion — используемый тестовый пример сделал ветку «неизменной» из ствола, однако Subversion регистрирует коммит, и этот коммит является шумным по сравнению с тем, что должно быть:

ph7785:svn_workingcopy paul$ svn diff -r 3:4
Index: branches/br1/file.txt
===================================================================
--- branches/br1/file.txt(revision 0)
+++ branches/br1/file.txt(revision 4)
@@ -0,0 +1,5 @@
+Efficiently unleash cross-media information without
+cross-media value. Quickly maximize timely
+deliverables for real-time schemas. Dramatically
+maintain clicks-and-mortar solutions without
+functional solutions.

Эти строки не изменились! Тигель исправляет это для вас, с некоторой умной двойной проверкой:

Без этого исправления Crucible для (или маскировки) ошибки Subversion вы могли бы в конечном итоге пересмотреть все, что происходило в транке, снова, даже если вы уже рассматривали это раньше.

Есть еще один snafu в Subversion, в котором слияние с ревизией 5 на стволе не показано в ревизии 7 на ветке br1. Я добавил красную стрелку, чтобы показать, как это должно выглядеть:

Atlassian отметили некоторые ограничения в статье Просмотр графика коммитов для репозитория (найдите «subversion» на странице). Они могут, я подозреваю, устранить эту ошибку.

Столкновение слияния (и Ultimate Commit) — # 7

Два изменения в примечании

Строка 1 — по сравнению с общим предком, который сказал «кросс-медиа», ствол (до слияния) был изменен на «разделенный по полу». По сравнению с общим предком ветвь br1 (до слияния) была изменена на «социально неловкая». Оба эти значения ничего не значат, поскольку мы выбрали вместо этого «окрашенный».

Строка 3 — В сравнении с общим предком, у которого были «щелчки и миномет», ствол (до слияния) был изменен на «эмпирический» в изменении № 5. Ветвь не имела изменений по сравнению с общим предком. Это объединение показывает «эмпирическое» изменение как изменение в # 7, которое технически это (ранее не было в ветке br1), но оно могло быть рассмотрено ранее как часть обзора для # 5.

Было бы здорово, если бы Crucible мог показать, что он был в предыдущем коммите и даже пересмотрен с другим цветом.

Одно изменение не очевидно

Ранее это происходило на ветке, но «немедленный» (строка 2) изменился на «любой». Это правильно, что это не показано.

Тигель с мерзавцем

Сделайте новое Git-репо внутри Crucible (запомните repo_name).

С помощью этого скрипта (git_test.sh):

#!/bin/sh
rm -rf git_workingcopy
git clone $1 git_workingcopy
cd git_workingcopy
echo "** Create trunk version of file"
echo "Efficiently unleash cross-media information without\nimmediate value. Dramatically\nmaintain clicks-and-mortar solutions without\nfunctional aspects." > file.txt
git add file.txt
git commit -m "initial version on master" 
echo "** Create Branch .."
git branch br1 
git checkout br1
if [ "$2" = "mod" ]; then
  echo "** .. With small Modification during branching"
  perl -p -i -e "s/aspects/bits and pieces/g" file.txt
fi
echo "** Modify trunk version of file"
git checkout master 
perl -p -i -e "s/cross-media/sexed-up/g" file.txt
perl -p -i -e "s/clicks-and-mortar/empirical/g" file.txt
git commit -am "follow-up change on master" 
git checkout br1
echo "** Modify branch version of file"
perl -p -i -e "s/cross-media/socially-awkward/g" file.txt
perl -p -i -e "s/immediate/any/g" file.txt
git commit -am "divergent and potentially clashing modification on the branch"
echo "** Merge trunk to branch at HEAD, preserving 'any' from the br1 ancestor, taking 'empirical' from the master, and choosing neither ancestor with 'tie-dyed'"
git merge --no-commit -s ours master 
echo "Efficiently unleash tie-dyed information without\nany value. Dramatically\nmaintain empirical solutions without\nfunctional aspects." > file.txt
if [ "$2" = "mod" ]; then
  perl -p -i -e "s/aspects/bits and pieces/g" file.txt
fi
git commit -am "merge from trunk to branch, with resolved conflict (and an auto-merge)"
echo "you'll do the push yourself - master AND br1 :)" 

Заполните тестовые коммиты в репо:

./git_test.sh http://127.0.0.1:8060/git/repo_name.git

Теперь перейдите к графу Commit для этого:

Заметки:

  1. Those lines look like they are crossing. It’s just a display glitch in Cruicible – it’s a rhombus really.
  2. The modification to trunk, on an implied timeline looks to have happened chronologically before the branch was made, and that was not the case. Again, that’s a display choice in Crucible that’s harmless.
  3. There’s only four commits. The branch creation isn’t shown as a commit (normal Git behavior), and only the first change on that branch is shown.
  4. Unlike for Subversion, Crucible shows the merge line from the latest one the trunk to the latest on the branch.

Let’s look at the merge commit:

Arghh, it’s a nightmare, it’s not showing me the diff on the branch. The previous commit on the branch looks like this:

How can something change from ‘immediate’ to ‘any’ in two successive commits on the same branch? The problem is that, in Crucible at least, the parent of the merge was the last version on the trunk, not the last version on the master branch.

Crucible with Perforce

Same as Subversion. I didn’t make a script, although it would have been only 15% longer than the Subversion one due to increased command-line setup complexity. Caveats: the initial branch population in Crucible – clicking on the diff does nothing (can’t see anything). Atlassian note there are limitations in Perforce too.

No pics, as I’m all picced out for now.

Conclusion

With Crucible and any of Subversion/Git/Perforce there were scenarios that would lead to changes being reviewed twice. Really though it’s going to be a very small percentage of cases, and you shouldn’t worry about it. I have not explored, but it could be that there are things that don’t get reviewed at all, in a multi-branch code-review design.

I can’t help but feel that Google’s linear and “Continuous Review” is simply the most efficient way to do reviews. Sure we can’t do roll-up reviews (not otherwise covered in this article) by methodically reviewing every commit soon after they happen, but I’m not sure I’d want to if I’m losing all the context that comes with the commit messages.

Actually, I’m sure a Trunk Based Development workflow (regardless of your local-branching model), with reviews of the commits on the trunk only and ASAP is best. “Continuous Review” isn’t my coin by the way, but I like it.