X-Git-Url: http://andersk.mit.edu/gitweb/svn-all-fast-export.git/blobdiff_plain/688d69ec473b06fb767cf29b62d66e9642c19a91..266afd58cc06fcc9a529e7bb26d53c3728887432:/src/svn.cpp diff --git a/src/svn.cpp b/src/svn.cpp index aeca0fc..8d5836b 100644 --- a/src/svn.cpp +++ b/src/svn.cpp @@ -57,12 +57,15 @@ typedef QHash IdentityHash; class AprAutoPool { apr_pool_t *pool; + AprAutoPool(const AprAutoPool &); + AprAutoPool &operator=(const AprAutoPool &); public: inline AprAutoPool(apr_pool_t *parent = NULL) { pool = svn_pool_create(parent); } inline ~AprAutoPool() { svn_pool_destroy(pool); } + inline void clear() { svn_pool_clear(pool); } inline apr_pool_t *data() const { return pool; } inline operator apr_pool_t *() const { return pool; } }; @@ -167,7 +170,7 @@ static int pathMode(svn_fs_root_t *fs_root, const char *pathname, apr_pool_t *po // maybe it's a symlink? SVN_ERR(svn_fs_node_prop(&propvalue, fs_root, pathname, "svn:special", pool)); - if (strcmp(propvalue->data, "symlink") == 0) + if (propvalue && strcmp(propvalue->data, "symlink") == 0) mode = 0120000; return mode; @@ -192,7 +195,7 @@ static svn_stream_t *streamForDevice(QIODevice *device, apr_pool_t *pool) } static int dumpBlob(Repository::Transaction *txn, svn_fs_root_t *fs_root, - const char *pathname, apr_pool_t *pool) + const char *pathname, const QString &finalPathName, apr_pool_t *pool) { // what type is it? int mode = pathMode(fs_root, pathname, pool); @@ -200,7 +203,7 @@ static int dumpBlob(Repository::Transaction *txn, svn_fs_root_t *fs_root, svn_filesize_t stream_length; SVN_ERR(svn_fs_file_length(&stream_length, fs_root, pathname, pool)); - QIODevice *io = txn->addFile(pathname, mode, stream_length); + QIODevice *io = txn->addFile(finalPathName, mode, stream_length); #ifndef DRY_RUN // open the file @@ -218,6 +221,49 @@ static int dumpBlob(Repository::Transaction *txn, svn_fs_root_t *fs_root, return EXIT_SUCCESS; } +static int recursiveDumpDir(Repository::Transaction *txn, svn_fs_root_t *fs_root, + const QByteArray &pathname, const QString &finalPathName, + apr_pool_t *pool) +{ + // get the dir listing + apr_hash_t *entries; + SVN_ERR(svn_fs_dir_entries(&entries, fs_root, pathname, pool)); + AprAutoPool dirpool(pool); + + for (apr_hash_index_t *i = apr_hash_first(pool, entries); i; i = apr_hash_next(i)) { + dirpool.clear(); + const void *vkey; + void *value; + apr_hash_this(i, &vkey, NULL, &value); + + svn_fs_dirent_t *dirent = reinterpret_cast(value); + QByteArray entryName = pathname + '/' + dirent->name; + QString entryFinalName = finalPathName + '/' + dirent->name; + + if (dirent->kind == svn_node_dir) { + if (recursiveDumpDir(txn, fs_root, entryName, entryFinalName, dirpool) == EXIT_FAILURE) + return EXIT_FAILURE; + } else if (dirent->kind == svn_node_file) { + if (dumpBlob(txn, fs_root, entryName, entryFinalName, dirpool) == EXIT_FAILURE) + return EXIT_FAILURE; + } + } +} + +static bool wasDir(svn_fs_t *fs, int revnum, const char *pathname, apr_pool_t *pool) +{ + AprAutoPool subpool(pool); + svn_fs_root_t *fs_root; + if (svn_fs_revision_root(&fs_root, fs, revnum, subpool) != SVN_NO_ERROR) + return false; + + svn_boolean_t is_dir; + if (svn_fs_is_dir(&is_dir, fs_root, pathname, subpool) != SVN_NO_ERROR) + return false; + + return is_dir; +} + time_t get_epoch(char *svn_date) { struct tm tm; @@ -229,20 +275,20 @@ time_t get_epoch(char *svn_date) int SvnPrivate::exportRevision(int revnum) { - AprAutoPool pool(global_pool); + AprAutoPool pool(global_pool.data()); // open this revision: + qDebug() << "Exporting revision" << revnum; svn_fs_root_t *fs_root; SVN_ERR(svn_fs_revision_root(&fs_root, fs, revnum, pool)); - qDebug() << "Exporting revision" << revnum; // find out what was changed in this revision: QHash transactions; apr_hash_t *changes; SVN_ERR(svn_fs_paths_changed(&changes, fs_root, pool)); - AprAutoPool revpool(pool); + AprAutoPool revpool(pool.data()); for (apr_hash_index_t *i = apr_hash_first(pool, changes); i; i = apr_hash_next(i)) { - svn_pool_clear(revpool); + revpool.clear(); const void *vkey; void *value; @@ -252,16 +298,35 @@ int SvnPrivate::exportRevision(int revnum) // is this a directory? svn_boolean_t is_dir; SVN_ERR(svn_fs_is_dir(&is_dir, fs_root, key, revpool)); - if (is_dir) - continue; // Git doesn't handle directories, so we don't either + if (is_dir) { + // was this directory copied from somewhere? + svn_revnum_t rev_from; + const char *path_from; + SVN_ERR(svn_fs_copied_from(&rev_from, &path_from, fs_root, key, revpool)); + + if (path_from == NULL) + // no, it's a new directory being added + // Git doesn't handle directories, so we don't either + continue; + + qDebug() << "..." << key << "was copied from" << path_from; + } QString current = QString::fromUtf8(key); // find the first rule that matches this pathname bool foundMatch = false; - foreach (Rules::Match rule, matchRules) + foreach (Rules::Match rule, matchRules) { + if (rule.minRevision > revnum) + continue; + if (rule.maxRevision != -1 && rule.maxRevision < revnum) + continue; if (rule.rx.exactMatch(current)) { foundMatch = true; + if (rule.repository.isEmpty()) + // ignore rule + break; + QString repository = current; QString branch = current; QString path = current; @@ -271,8 +336,8 @@ int SvnPrivate::exportRevision(int revnum) branch.replace(rule.rx, rule.branch); path.replace(rule.rx, rule.path); - qDebug() << "..." << current << "->" - << repository << branch << path; + qDebug() << "..." << qPrintable(current) << "rev" << revnum << "->" + << qPrintable(repository) << qPrintable(branch) << qPrintable(path); Repository::Transaction *txn = transactions.value(repository, 0); if (!txn) { @@ -284,8 +349,8 @@ int SvnPrivate::exportRevision(int revnum) } QString svnprefix = current; - if (current.endsWith(path)) - current.chop(path.length()); + if (svnprefix.endsWith(path)) + svnprefix.chop(path.length()); txn = repo->newTransaction(branch, svnprefix, revnum); if (!txn) @@ -297,21 +362,30 @@ int SvnPrivate::exportRevision(int revnum) svn_fs_path_change_t *change = reinterpret_cast(value); if (change->change_kind == svn_fs_path_change_delete) txn->deleteFile(path); + else if (!is_dir) + dumpBlob(txn, fs_root, key, path, revpool); else - dumpBlob(txn, fs_root, key, revpool); + recursiveDumpDir(txn, fs_root, key, path, revpool); break; } + } if (!foundMatch) { - qCritical() << current << "did not match any rules; cannot continue"; - return EXIT_FAILURE; + if (is_dir) { + qDebug() << current << "is a directory; ignoring"; + } else if (wasDir(fs, revnum - 1, key, pool)) { + qDebug() << current << "was a directory; ignoring"; + } else { + qCritical() << current << "did not match any rules; cannot continue"; + return EXIT_FAILURE; + } } } - svn_pool_clear(revpool); + revpool.clear(); if (transactions.isEmpty()) - return true; // no changes? + return EXIT_SUCCESS; // no changes? // now create the commit apr_hash_t *revprops;