Subversion更新外部引用到指定日期

40

我正在处理一个由SVN控制的大型已建立项目。 代码库的许多部分作为外部引用进行检出,但是由其他人积极开发。
我想要更新整个工作副本(包括所有的外部引用),以便在特定时间点反映出各个存储库的HEADS。 我最初的尝试是:

svn up -r'{20090324}'

这会将当前目录更新到指定日期,但将所有外部内容更新到当前日期。逐个更新外部内容可以按预期工作。
我知道由于外部内容的特殊性质,使用单个修订版本号无法进行更新,但为什么使用日期也不起作用呢?
有没有最好的方法实现我所寻找的按时间点的效果,而不必维护一个硬编码各种外部内容的脚本?

我正在运行Linux系统。


5
+1非常好的问题。就个人而言,我开始认为外部应该从未被允许跟踪其他存储库的HEAD。始终指定外部的确切修订版本会更加安全。 - Wim Coenen
13个回答

43

这种方法效率低下,因为它比通常需要的更频繁地调用svn update。否则,它是简短而甜美的:

Unix:

find . -name .svn -execdir svn update -r {2010-08-30} \;

Windows:

forfiles /m .svn /s /c "cmd /c svn up -r {2010-08-30}"

1
那工作得相当不错。在大型目录树上有点慢,但它完成了工作。并且有额外的好处是它足够小以便记住。感谢您注册解决此问题! - Whatsit
4
这不会影响单个文件的外部 - 它们将保持其最新版本。 - Izkata
我之前不知道forfiles会在当前目录下启动cmd,这一点可以通过执行forfiles /m .svn /s /c "cmd /c cd"来证实。即使我找不到任何文档说明它会这样做...但是实验结果表明它确实会这样做! - Wyck
1
这肯定会更新每个外部两次(或更多),一次来自顶级目录,然后在更新外部本身时?因为缺少--ignore-externals标志,如maniek的解决方案所示。 - Stein
使用PowerShell,命令可以是: ls -r -dir -hidden | where name -eq .svn | %{svn up --ignore-externals -r {2010-08-30} $_.parent.fullname} - Stein
非常感谢。我在这里发现了一个问题:如果初始工作副本具有指向目标日期尚不存在的文件夹的svn:external,则该文件夹的svn up将失败。错误消息将类似于“svn:E160005:目标路径'xyz'不存在”。在这种情况下,您通常只需要删除该文件夹。 - Pieter-Jan Busschaert

9
使用svn:externals时,通常不建议使用没有修订号的外部引用。这意味着很难将外部版本与包含项目的版本相对应;我是通过尝试跟踪包含外部引用的项目的历史记录而知道的,我必须猜测哪个修订版本对应于包含项目中的修订版本(有时它比包含项目早,因为有人更新了外部项目然后更新了包含项目,有时它比包含项目晚,因为有人直接在外部检出中编辑文件然后提交)。
相反,正如在subversion书籍的externals章节中的提示框所建议的,您应该始终使用修订号提交外部引用。这样,每当您检出包含项目的特定修订版本时,也会检出外部引用的相应修订版本。这确实需要更多的工作,因为您必须每次更新svn:externals属性中的修订号(我们编写了一个自动执行此操作的脚本),但从长远来看,这是一个更好的解决方案。
编辑:这里是我们使用的脚本(一个rake任务)的骨架,方便更新外部引用并保持所有内容同步。
desc 'Update external for this project (rake update_external r=17789)'
task :update_external do |t|
  rev = ENV['r']
  rev =~ /^\d+$/ or raise "Invalid SVN revision number: r=<#{rev}>"

  # Update the project.
  sh "svn update"

  URL = 'svn+ssh://example.com/external/trunk'
  sh "svn propset svn:externals 'external -r#{rev} #{URL}' containing/directory"

  # Update again -- to put the externals back to the right revision.
  sh "svn update"
end

不幸的是,我没有太多改变当前设置的机会,尽管我非常喜欢使用脚本自动更新外部修订号的想法。尽管如此,我会将这个问题保持开放一段时间,因为我相信还有其他人和我处于同样的境地。 - Whatsit
@Brian 你的脚本在什么时候执行?是手动运行还是自动化运行? - Tinister

3

这个是我迄今为止找到的最佳解决方案(这是一个棘手的问题 - 子版本开发人员应该在核心中修复它)。这个例子特别处理mplayer,但你应该很容易看出逻辑。

; fetch the rev I want without including the externals
svn checkout -r "$REV" --ignore-externals \
    svn://svn.mplayerhq.hu/mplayer/trunk

; grab the date of that rev from the svn info output
DATE=`svn info trunk|sed -n '/^Last Changed Date/s/.*: \(.*\) (.*/\1/p'`

; fetch the externals using that date
svn checkout -r "{$DATE}" \
        svn://svn.mplayerhq.hu/ffmpeg/trunk/libavutil \
        svn://svn.mplayerhq.hu/ffmpeg/trunk/libavformat \
        svn://svn.mplayerhq.hu/ffmpeg/trunk/libavcodec \
        svn://svn.mplayerhq.hu/ffmpeg/trunk/libpostproc

2

基于Dave Cohen的回答,但速度要快得多:

find . -name '.svn' -execdir svn up --ignore-externals -r '{2014-02-05}' \;

2
我还没有找到完美的解决方案,但这个方案接近完美:

svn propget svn:externals | sed -e 's/ .*$//g' | xargs svn up -r'{20090324}'

在我的情况下,这个方法有效,因为没有递归的外部依赖项,并且所有外部依赖项都在目录中定义,或者使用修订号定义,因此正则表达式可以轻松地去掉尾部存储库路径。
虽然我相信有更好的正则表达式可以通用解决问题。
编辑:实际上,我越想,就越发现问题。最大的问题是它使用了当前版本的svn:externals,而不是指定日期的svn:externals。这比我最初想到的还要复杂。

1

很好用的工具,可以冻结给定路径下的外部库。我们在从主干创建标签后使用此工具来冻结外部库:

http://svnxf.codeplex.com/


1

这有点棘手,我很抱歉无法为您当前的情况提供一个好的解决方案 - 但是Brian已经给出了避免它的答案。

避免这种情况需要一点仓库理论 - 基本上,在没有对主干进行相应修订的情况下,必须不能修改项目任何源代码。

通过将所有外部指向标记或特定版本,不会在未提交外部引用更改的情况下出现任何来自它们的更改。但是,如果将外部指向移动的主干,则对外部的更改将完全不会显示在主项目的时间线中 - 这就使您处于当前的位置。

个人而言,我认为外部应被视为独立项目并发布,因此所有外部都指向标记。在进行大量并行开发时,可以“切换”外部到主干,或者暂时将不稳定的开发分支指向外部主干,但主干项目总是指向稳定的外部,并且升级是一个有意识的决定。这种观点可能对您的情况过于严格,但值得看看其他可能性。


0

让svn为您执行递归操作。

临时文件和tee仅用于让您看到完整的输出:

SVN_UP_OUTPUT=$(mktemp SVN_UP_OUTPUT.XXXXX)
svn up -r$REVISION | tee $SVN_UP_OUTPUT
cat $SVN_UP_OUTPUT | egrep '^Fetching external' | egrep -o "'.*'" | sed -e "s/'//g" | while read DIR;do
    echo $$ svn up -r$REVISION "$DIR"
    svn up -r$REVISION "$DIR"
done
rm $SVN_UP_OUTPUT

如果您不关心输出,可以缩短为以下代码:
svn up -r$REVISION | egrep '^Fetching external' | egrep -o "'.*'" | sed -e "s/'//g" | while read DIR;do
    svn up -r$REVISION "$DIR"
done

当然,在你的情况下:

REVISION='{20090324}'

注意:在添加/删除外部组件时可能会出现问题。 - Izkata

0

0

当您更新主WC时,您正在更新的修订版/日期等不会传递到在更新时进行更新的外部。在外部定义中没有指定特定的修订版的情况下,它们将始终跟踪它们所指向的任何内容的头部。如果您在此处指定了修订版,则这是您将获得的唯一修订版。我非常确定您尝试做的事情是不可能的-这是我尝试解决我遇到的问题的方法,正如我在this question中描述的那样。(虽然我从未解决过那个问题,但我认为在那里提到的代理想法可以解决它。但它可能对您没有帮助)


你应该觉得自己很幸运,因为你有意识地提前看到了这些问题——而我直到不得不一次性回滚所有内容时才突然意识到这个问题。 - Whatsit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接