如何列出subversion存储库中的所有文件,离线?

时间:2016-03-06 12:28:56

标签: svn

通常情况下,我会使用svn list -R .查找我已检出的subversion存储库中的所有文件。

然而,对于像LLVM这样的大型回购,它只是太慢了。

$ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
... lots of files
 U   llvm
Checked out revision 262801.
$ cd llvm
$ time svn list -R . > files.txt
svn list -R . > files.txt  0.79s user 0.13s system 0% cpu 1:50.73 total

差不多两分钟!我可以使用find做得更好(虽然我之后需要过滤掉.svn):

$ time find . -type f > find_files.txt 
find . -type f > find_files.txt  0.02s user 0.02s system 98% cpu 0.040 total

似乎subversion正在为repo中的所有20,000个文件发出网络请求!如果我删除了我的网络连接:

$ time svn list -R . > files_offline.txt
svn: E170013: Unable to connect to a repository at URL 'http://llvm.org/svn/llvm-project/llvm/trunk'
svn: E670002: Name or service not known
svn list -R . > files_offline.txt  0.02s user 0.01s system 41% cpu 0.056 total

我可以在不使用网络的情况下列出本地subversion结帐中的所有文件吗?

2 个答案:

答案 0 :(得分:3)

UPD :如果您是Windows用户,可以从此处下载svncmd工具:https://sf.net/p/svncmd/scripts/HEAD/tree/trunk(按钮Download snapshot)。 请在此处查看svn_list.bat脚本:https://sf.net/p/svncmd/scripts/HEAD/tree/trunk/Scripts/svn_list.bat

使用示例

  1. call svn_list.bat -offline branch/current > files.lst
  2. call svn_list.bat -offline -wcroot branch/current branch/current/myproject > files.lst
  3. SVN wc.db是sqlite数据库。您可以使用“Commandite Shell For SQLite”直接访问SVN数据库文件:https://www.sqlite.org/cli.html

    例如,1.9.4数据库实现包含这些表:

    • NODES
    • NODES_CURRENT
    • NODES_BASE

    表之间的差异在于对存储库上次提交的更改:

    • NODES_BASE表存储与特定修订版本相关的本地文件和文件夹的基本状态。例如,在将工作副本更新到特定修订版但在本地更改取代之前,您将获得该状态。
    • NODES_CURRENT表存储与NODES_BASE存储相同的条目减去带有文件/文件夹的条目,这些条目将在提交后从NODES_BASE中删除,并且在提交之前加上请求添加/删除NODES_BASE的文件/文件夹的条目。这意味着请求删除的文件/文件夹条目只会在表格中显示一次(没有条目重复)。
    • NODES表存储与NODES_BASE存储相同的条目以及请求添加/删除NODES_BASE的文件/文件夹的条目。这意味着请求删除的文件/文件夹条目将在表格中显示两次,因为请求删除的条目已经存在于NODES_BASE表中。

    因此,如果您希望基本文件列表包含本地更改条目,则必须使用NODES_CURRENT表。如果您想要没有本地更改条目的基本文件列表,则必须使用NODES_BASE表。

    以下是Windows批处理shell的用法示例:

    - 没有任何本地更改的基本状态:

    sqlite3.exe -batch wc.db ".headers off" "select local_relpath from nodes_base where local_relpath != ''"
    

    - 使用已删除文件和文件夹的条目更改状态:

    sqlite3.exe -batch wc.db ".headers off" "select local_relpath from nodes_current where local_relpath != ''"
    

    - 更改了没有删除文件和文件夹条目的状态

    sqlite3.exe -batch wc.db ".headers off" "select local_relpath from nodes_current where local_relpath != '' and presence = 'normal'"
    

    提示 要查找wc.db文件,例如,通过F3键直接从Total Commander查找,您可以安装sqliteviewer插件。

    <强> TIP2: 您必须在工作副本目录树(递归)的路上转储每个“.svn / wc.db”文件,以将所有文件列表作为“svn list -R”。确实

    <强> TIP3: “svn list”转储NODES_BASE条目(第一个示例)。

    <强> TIP4: “svn list”的输出与local_relpath字段的输出略有不同,因为:

    • 目录存储时没有斜杠。
    • 在Windows控制台中,utf-8不是默认代码页,sqlite可执行文件将使用当前控制台代码页来翻译输出。
    • “svn list”确实使用当前的语言环境输出和稍微不同的字符串升序排序。

    -

    如果要至少更正utf-8输出和尾部斜杠,例如,在Windows控制台中,您必须编写一组命令并使SQL请求复杂化:

    chcp 65001
    sqlite3.exe -batch wc.db ".headers off" "with new_nodes as ( select case when kind != 'dir' then local_relpath else local_relpath || '/' end as new_paths from nodes_base where local_relpath != '') select * from new_nodes order by new_paths asc"
    

    输出将几乎关闭到“svn list”输出,除了带有'/','。'的行之间的错误顺序。和''角色在同一个位置。

答案 1 :(得分:1)

svn status -v

但是可以为您提供所需的信息 - 它需要一些预处理给定的输出。以下是C#的一些代码片段:

/// <summary>
/// Lists all files being under svn version control, recursively. Does not include directories.
/// 
/// Does not list newly added files (Which are not yet committed)
/// </summary>
/// <param name="path">Path where to take files</param>
/// <returns>List of files</returns>
static public List<String> svnGetFileList( String path, bool bRecursive = true)
{
    // If you're online - this kind of line can be used.
    //List<String> l = ExecCmd("svn ls \"" + path + "\" " + ((bRecursive) ? "--recursive" : "")).Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).ToList();
    String svnStatusText = ExecCmd("svn status -v \"" + path + "\" ");

    List<String> l = Regex.Matches(svnStatusText, "^[^?].{6} +[0-9]+ +[0-9]+ +(.*?) +([^\r\n]+)", RegexOptions.Multiline).Cast<Match>().Select(x => x.Groups[2].Value).ToList();
    // [^?] - all versioned files only - status must not contain '?' - unversioned.
    // skip 6 character columns (one character wide).
    // skip 2 svn revisions
    // skip svn login (.*) - non greedy match.

    for (int i = 0; i < l.Count; i++)
    {
        if (l[i].Length <= path.Length)             // Only for first entry (directory name)
        {
            l.RemoveAt(i);
            i--;
            continue;
        }

        if (Directory.Exists(l[i]))
        {
            l.RemoveAt(i);
            i--;
            continue;
        }

        l[i] = l[i].Substring(path.Length + 1);     // Cut off main path.

        if (!bRecursive && l[i].Contains("\\"))     // Don't list sub-folders
        {
            l.RemoveAt(i);
            i--;
            continue;
        }
    }
    return l;
}



/// <summary>
/// Executes command and throw exception if something unexpected happened.
/// </summary>
/// <param name="cmd">command to execute</param>
/// <returns>String logged from standard output and standard error</returns>
public static String ExecCmd(String cmd)
{
    String error = "";
    if (!ExecCmd(cmd, ref error))
    {
        throw new Exception(error);
    }

    return error;
} //ExecCmd


/// <summary>
/// Executes command and returns standard output & standard error to error string.
/// </summary>
/// <returns>true if error code was 0, false if something else</returns>
public static bool ExecCmd(String cmd, ref String error)
{
    Process p = new Process();
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.FileName = "cmd.exe";
    // Whole command should be quoted.
    // cmd.exe /C ""mytool.exe" "c:\mypath\myfile.txt""
    //            ^                                   ^                                   ^
    // https://social.msdn.microsoft.com/forums/vstudio/en-US/03ea84cf-19a6-450d-a3d6-8a139857e0cd/help-with-paths-containing-spaces
    //
    p.StartInfo.Arguments = "/C \"" + cmd + "\" 2>&1";
    // Console.WriteLine("Executing 'cmd.exe " + p.StartInfo.Arguments + "'");
    p.Start();
    error = p.StandardOutput.ReadToEnd();
    p.WaitForExit();

    if (p.ExitCode != 0)
    {
        error += "\r\n  (Executed command was: " + cmd + ")";
        return false;
    }

    return true;
} //ExecCmd