Отслеживание вариантов исходного кода - PullRequest
8 голосов
/ 21 сентября 2009

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

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

Альтернативы, которые я могу придумать:

  • определяет . Предварительная обработка. Плюсы: все всегда присутствует в исходном коде, сложнее пропустить обновление одного из продуктов. Минусы: сложнее читать. Это может быть хорошо, пока у нас есть только два варианта, когда станет четыре или более, это будет боль. Кроме того, кажется сложнее применить принцип СУХОЙ (не повторяй себя).

  • одна ветвь для варианта продукта . Если включены изменения, относящиеся ко всем продуктам, они должны быть объединены с другими продуктами. Минусы: если коммит содержит как изменения для всех продуктов, так и изменения для конкретного варианта, возникнут проблемы. Конечно, вы можете убедиться, что коммит содержит только один вид изменений: изменения этого продукта или изменений всей семьи. Но попытаться навязать это команде? Плюс, тогда слияние не сработает, вместо этого мы должны собирать вишню. Верно?

  • a основной репозиторий в качестве подмодуля . Сделайте все файлы, которые содержат основные функциональные возможности, самим хранилищем. Все продукты содержат версию основного репозитория в качестве субмодуля. Минусы: я не вижу, чтобы в конечном итоге не было вариантов основного подмодуля. Тогда у нас снова будут проблемы, и тогда мы снова будем использовать определения или что-то плохое. Основной репозиторий с ветками? Затем мы возвращаемся к предыдущему варианту: изменение, которое применяется ко всем ветвям, должно быть объединено, но объединение включает в себя также материал для конкретного продукта.

  • создать репозиторий на модуль . Например, репозиторий для драйвера дисплея, еще один для оборудования управления питанием, еще один для интерфейса пользовательского ввода, ... Плюсы: хорошая модульность. Создайте новый продукт, просто подбирая нужные вам модули как подмодули! Все подмодули могут иметь ветви, если, например, вариант использует оборудование по-другому. Минусы: множество модулей, каждый из которых отслеживает пару файлов (включаемый файл и исходный файл). Хлопот. Кто-то делает важное обновление в каком-то модуле? Затем кто-то должен включить изменения в другие ветви этого модуля, если это необходимо. Затем кто-то должен также обновить субмодуль в каждом репозитории продуктов. Довольно трудоемко, и мы как бы теряем часть снимка git.

Как вы это делаете и как это работает? Или как бы ты это сделал?

У меня такое чувство, что я должен иметь опыт сбора вишни.

Ответы [ 6 ]

6 голосов
/ 21 сентября 2009

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

Преимущество этого состоит в том, что при работе с конкретным вариантом вы видите весь его код вместе, без кода других вариантов, чтобы запутать вещи. Читаемость также намного лучше, чем засорять источник с помощью #ifdef, #elif, #endif и т. Д.

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

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

5 голосов
/ 21 сентября 2009

Я не уверен, что это «лучшая практика», но проект Scintilla годами использует что-то, что все еще вполне поддается управлению. Он имеет только одну ветку, общую для всех платформ (в основном Windows / GTK + / Mac, но с вариантами для VMS, Fox и т. Д.).

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

Но основным механизмом переносимости является использование ОО: он абстрагирует некоторые операции (рисование, отображение контекстного меню и т. Д.) И использует файл платформы (или несколько) для каждой цели, обеспечивая конкретную реализацию.
Makefile компилирует только нужные файлы и использует ссылки для получения правильного кода.

5 голосов
/ 21 сентября 2009

Я бы попытался пройти как можно больше #define с. С помощью правильного кода вы можете минимизировать влияние на читаемость и количество повторений.

Но в то же время #define подход можно смело комбинировать с разбиением и ветвлением, применение которых зависит от природы кодовой базы.

2 голосов
/ 21 сентября 2009

Я думаю, что соответствующий ответ отчасти зависит от того, насколько радикально отличаются варианты.

Если есть небольшие части, которые отличаются, целесообразно использовать условную компиляцию для одного исходного файла. Если варианты реализации согласованы только в интерфейсе вызова, то может быть лучше использовать отдельные файлы. Вы можете включить радикально вариантные реализации в один файл с условной компиляцией; насколько грязно это зависит от объема варианта кода. Если это, скажем, четыре варианта по 100 строк в каждом, возможно, один файл в порядке. Если это четыре варианта по 100, 300, 500 и 900 строк, то один файл, вероятно, плохая идея.

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

#include "config.h"
#if defined(USE_VARIANT_A)
#include "variant_a.c"
#elif defined(USE_VARIANT_B)
#include "variant_b.c"
#else
#include "basecase.c"
#endif
1 голос
/ 22 сентября 2009

Вас ждет мир боли!

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

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

Если вы пойдете по маршруту #define, я бы поставил следующее место, где проверяется вариант:

#else 
    #error You MUST specify a variant!
#endif

Это обеспечит сборку всех файлов для одного и того же варианта в процессе сборки.

0 голосов
/ 10 апреля 2011

Я столкнулся с той же проблемой. Я хочу поделиться источником не только между целями, но и между платформами (например, Mono vs. Visual studio. Кажется, что нет никакого способа легко сделать это с помощью только версии, предоставляемой Git для ветвления / тегирования. Потому что в идеале вы Хотелось бы поддерживать общую ветвь кода, а также хосты / целевые ветви и просто объединять общий код с этими ветвями и из них. Но я не знаю, как это сделать. Может быть, сочетание разветвления и пометки? Кто-нибудь?

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

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

...