Я прочитал книгу подверсии, и мне ясно, что подверсия не хранит отдельные файлы, но только дельты для уменьшения дискового пространства. Подверсия также делает то же с двоичными файлами также (это раньше было огромной слабостью CVS).
Однако я не понимаю точный механизм. Когда я фиксирую файл, что происходит?
Первый случай мог бы казаться самым логическим. Это однако поднимает другой вопрос. Если я имею в репозитории подверсии, файл с 1 000 фиксаций и новым разработчиком проверяет чистую копию, то подверсия должна была бы выбрать исходную версию (начальный импорт) и применить 1000 diffs на это прежде, чем возвратить результат. Это корректно? Есть ли своего рода кэширование для файлов, где последняя версия сохранена также?
В основном, где я могу найти информацию о внутренностях репозитория SVN?
Обновление: По-видимому, бэкенд подверсии играет большую роль в этом. В то время или запись FSFS использует опцию 1, в то время как BDB использует опцию 2. Спасибо msemack!
То, что требуется, может быть реализовано с помощью Boost.Variant .
Идея состоит в том, чтобы определить новый тип итератора, в котором хранится вариант (например, объединение C на стероидах), содержащий либо прямой, либо обратный итератор:
template<class InputRange>
struct any_dir_iterator
: std::iterator_traits<typename boost::range_iterator<InputRange>::type> {
typedef typename boost::range_iterator<InputRange>::type forward_iterator;
typedef typename
boost::range_reverse_iterator<InputRange>::type reverse_iterator;
typedef boost::variant<forward_iterator, reverse_iterator> iterator_type;
iterator_type current_it, end_it;
any_dir_iterator(InputRange & input_range,
bool fwd = true,
bool end = false)
{
end_it = fwd ? iterator_type(boost::end(input_range))
: iterator_type(boost::rend(input_range));
if(end)
current_it = end_it;
else
current_it = fwd ? iterator_type(boost::begin(input_range))
: iterator_type(boost::rbegin(input_range));
}
reference operator*() const {
return boost::apply_visitor(dereference_visitor<any_dir_iterator>(),
current_it);
}
any_dir_iterator & operator++() {
boost::apply_visitor(increment_visitor<any_dir_iterator>(),
current_it);
return *this;
}
bool operator==(any_dir_iterator const & rhs) {
return boost::apply_visitor(equals_visitor<any_dir_iterator>(),
current_it, rhs.current_it);
}
};
Это похоже на любой итератор Adobe , но гораздо менее общий, что означает, что он практически не будет иметь исполнения накладных расходов по сравнению с простым итератором.
Как вы можете видеть в коде выше, вся логика делегируется статическим посетителям, которые мы определяем следующим образом:
template<class AnyDirIterator>
struct dereference_visitor
: boost::static_visitor<typename AnyDirIterator::iterator_type> {
typedef typename AnyDirIterator::reference result_type;
template<class FwdOrRevIterator>
result_type operator()(FwdOrRevIterator const & it) const {
return *it;
}
};
template<class AnyDirIterator>
struct increment_visitor
: boost::static_visitor<typename AnyDirIterator::iterator_type> {
typedef void result_type;
template<class FwdOrRevIterator>
result_type operator()(FwdOrRevIterator & it) const {
++it;
}
};
template<class AnyDirIterator>
struct equals_visitor
: boost::static_visitor<typename AnyDirIterator::iterator_type>
{
typedef bool result_type;
template <typename FwdOrRevIterator>
bool operator()(FwdOrRevIterator const & lhs,
FwdOrRevIterator const & rhs) const {
return lhs == rhs;
}
template <typename T, typename U>
bool operator()( const T &, const U & ) const {
return false; // comparing fwd to rev or vice-versa
}
};
Это была сложная часть. Но мы все равно должны сделать это более удобным для использования, для чего мы определяем вспомогательную функцию, которая опирается на функциональность, предоставляемую Boost.Range library:
template<class InputRange>
boost::iterator_range<any_dir_iterator<InputRange> >
make_any_dir_range(InputRange & range, bool forward=true) {
typedef any_dir_iterator<InputRange> iterator;
return boost::make_iterator_range(iterator(range, forward),
iterator(range, forward, true));
}
И это все. Теперь вы можете написать:
int main() {
int items[] = { 1, 2 };
typedef std::vector<int> container_type;
container_type container(items, items + sizeof(items)/sizeof(items[0]));
BOOST_FOREACH(int i, make_any_dir_range(container, true))
std::cout << i << " ";
std::cout << "\n";
BOOST_FOREACH(int i, make_any_dir_range(container, false))
std::cout << i << " ";
return 0;
}
Какие отпечатки:
1 2
2 1
Это также работает с контейнерами const, хотя я не показал эту возможность в функции main
.
Еще одна хорошая вещь, которая является результатом использования Boost.Range, это то, что это работает с массивами из коробки. Так что вы можете сделать это:
int items[] = { 1, 2 };
BOOST_FOREACH(int i, make_any_dir_range(items, true)) // Prints "1 2"
std::cout << i << " ";
Держите этот ответ коротким я оставил несколько вещей, которые не реализованы (но все они являются шаблоном, не требующим новых посетителей):
Вот весь код в Codepad . Из-за политики «рассматривать предупреждения как ошибки» Codepad не проглотит его, но VS2008 и GCC 4.4 скомпилируют его ОК на моем локальном компьютере.
UPDATE
Я сделал несколько тестов и, очевидно, boost:: variant
действительно вводит некоторые накладные расходы времени выполнения: цикл на основе BOOST _ FOREACH
, подобный циклу в функции main
, работает примерно в 4 раз медленнее (при компиляции в режиме выпуска), чем эквивалентная версия с помощью простого итератора. Было бы интересно проверить, лучше или хуже, чем накладные расходы Adobe any _ iterator
.
Можно использовать класс DateTime, построенный в PHP. Он имеет метод под названием «add», и то, как он используется, подробно показано в руководстве: http://www.php.net/manual/en/datetime.add.php
Он, однако, требует PHP 5,3,0.
-121--696341-Поскольку формат репозитория Subversion является полностью внутренним, они могут изменять представление от одной версии к следующей. Я полагаю, что текущая редакция, как правило, хранит обратные дельты (ваш вариант 2), но также периодически хранит полные снимки, так что ему не нужно разрешать 1000 diffs перед возвращением результата.
Примечания к версии Subversion 1.6 содержат раздел Улучшения места хранения файловой системы , в котором содержатся некоторые примечания и ссылки на другие источники. Достаточно сказать, что детали места хранения данных Subversion сложны и могут быть изменены.
В исходном дереве Subversion также имеется конструкторский документ, описывающий использование пропуска дельт в Subversion . Как правило,каталог /notes/ содержит несколько полезных документов, касающихся внутренних устройств Subversion.
стандартная спецификация FSFS может вам помочь.
Или, если вы используете Berkeley DB, вот спецификация для этого.
FSFS использует обратные дельты для хранения изменений и пропуски-дельты для ускорения некоторых действий, если я все правильно понял.
Из документа Subversion Design (который, впрочем, довольно датирован) вы можете получить следующее:
Как и многие другие системы контроля ревизий, Subversion хранит изменения в виде различий. Она не делает полных копий узлов; вместо этого она хранит последнюю ревизию как полный текст, а предыдущие ревизии - как последовательность обратных дифов (слово "диф" здесь используется свободно - для файлов оно означает vdeltas, для каталогов - формат, выражающий изменения в каталогах).
Я не думаю, что это было изменено с тех пор.
Также смотрите Bubble-Up Method.
Каждый раз, когда вы фиксируете изменение, в хранилище сохраняет новую ревизию общего дерева хранилища, и помечает новое дерево новым номером ревизии. Конечно, большая часть дерева остается такой же, как и в предыдущей ревизии предыдущей, за исключением тех частей, которые вы изменили.
Новый номер ревизии - это последовательная метка, которая применяется ко всему новому дереву, а не только к файлам и каталогам, которые вы трогали в этой ревизии. Однако в разговорной речи номер ревизии используется для обозначения на изменение, внесенное в этой ревизии; например, "изменение в r588" ("r588" - это сокращение от "ревизия 588") на самом деле означает "разница между деревьями хранилища 587 и 588", или, говоря иначе, "изменение, внесенное в дерево 587 для создания дерева 588".
Взгляните на : Subversion FAQ