你能用 Subversion 做一个部分检出吗?

如果主干下有20个目录/每个目录中有很多文件,并且只需要其中的3个目录,那么是否有可能在主干下只使用这3个目录进行 Subversion 检出?

122059 次浏览

或者做一个非递归的签出/主干,然后只是对你需要的3个目录进行手动更新。

没什么特别有用的。你可以查看子树(就像 Bobby Jack 建议的那样) ,但是这样你就失去了自动更新/提交它们的能力; 要做到这一点,它们需要放置在它们的共同父节点下,一旦你查看了共同父节点,你就会下载该父节点下的所有内容。非递归不是一个好的选择,因为您希望更新和提交是递归的。

算是吧,就像鲍比说的:

svn co file:///.../trunk/foo file:///.../trunk/bar file:///.../trunk/hum

将获得文件夹,但是您将从颠覆透视图获得单独的文件夹。您必须分别提交和更新每个子文件夹。

我不相信您可以签出一个部分树,然后将部分树作为一个单独的实体处理。

事实上,感谢我在这里的帖子的评论,看起来 稀疏的目录是正确的选择。我认为应该这样做:

svn checkout --depth empty http://svnserver/trunk/proj
svn update --set-depth infinity proj/foo
svn update --set-depth infinity proj/bar
svn update --set-depth infinity proj/baz

或者,用 --depth immediates代替 empty检出 trunk/proj中没有内容的文件和目录。这样您就可以看到存储库中存在哪些目录。


正如@zigdon 的回答中提到的,您还可以执行非递归签出。这是一种较老的、不那么灵活的方法,可以达到类似的效果:

svn checkout --non-recursive http://svnserver/trunk/proj
svn update trunk/foo
svn update trunk/bar
svn update trunk/baz

Subversion 1.5引入了稀疏的签出,你可能会发现这些签出是有用的:

... ... 稀疏的目录(或 浅层结账) ... ... 允许您轻松地签出一个工作副本ーー或工作副本的一部分ーー比完全递归更为浅显,并且可以在以后自由地引入以前被忽略的文件和子目录。

我写了一个脚本来自动化复杂的稀疏校验。

#!/usr/bin/env python


'''
This script makes a sparse checkout of an SVN tree in the current working directory.


Given a list of paths in an SVN repository, it will:
1. Checkout the common root directory
2. Update with depth=empty for intermediate directories
3. Update with depth=infinity for the leaf directories
'''


import os
import getpass
import pysvn


__author__ = "Karl Ostmo"
__date__ = "July 13, 2011"


# =============================================================================


# XXX The os.path.commonprefix() function does not behave as expected!
# See here: http://mail.python.org/pipermail/python-dev/2002-December/030947.html
# and here: http://nedbatchelder.com/blog/201003/whats_the_point_of_ospathcommonprefix.html
# and here (what ever happened?): http://bugs.python.org/issue400788
from itertools import takewhile
def allnamesequal(name):
return all(n==name[0] for n in name[1:])


def commonprefix(paths, sep='/'):
bydirectorylevels = zip(*[p.split(sep) for p in paths])
return sep.join(x[0] for x in takewhile(allnamesequal, bydirectorylevels))


# =============================================================================
def getSvnClient(options):


password = options.svn_password
if not password:
password = getpass.getpass('Enter SVN password for user "%s": ' % options.svn_username)


client = pysvn.Client()
client.callback_get_login = lambda realm, username, may_save: (True, options.svn_username, password, True)
return client


# =============================================================================
def sparse_update_with_feedback(client, new_update_path):
revision_list = client.update(new_update_path, depth=pysvn.depth.empty)


# =============================================================================
def sparse_checkout(options, client, repo_url, sparse_path, local_checkout_root):


path_segments = sparse_path.split(os.sep)
path_segments.reverse()


# Update the middle path segments
new_update_path = local_checkout_root
while len(path_segments) > 1:
path_segment = path_segments.pop()
new_update_path = os.path.join(new_update_path, path_segment)
sparse_update_with_feedback(client, new_update_path)
if options.verbose:
print "Added internal node:", path_segment


# Update the leaf path segment, fully-recursive
leaf_segment = path_segments.pop()
new_update_path = os.path.join(new_update_path, leaf_segment)


if options.verbose:
print "Will now update with 'recursive':", new_update_path
update_revision_list = client.update(new_update_path)


if options.verbose:
for revision in update_revision_list:
print "- Finished updating %s to revision: %d" % (new_update_path, revision.number)


# =============================================================================
def group_sparse_checkout(options, client, repo_url, sparse_path_list, local_checkout_root):


if not sparse_path_list:
print "Nothing to do!"
return


checkout_path = None
if len(sparse_path_list) > 1:
checkout_path = commonprefix(sparse_path_list)
else:
checkout_path = sparse_path_list[0].split(os.sep)[0]






root_checkout_url = os.path.join(repo_url, checkout_path).replace("\\", "/")
revision = client.checkout(root_checkout_url, local_checkout_root, depth=pysvn.depth.empty)


checkout_path_segments = checkout_path.split(os.sep)
for sparse_path in sparse_path_list:


# Remove the leading path segments
path_segments = sparse_path.split(os.sep)
start_segment_index = 0
for i, segment in enumerate(checkout_path_segments):
if segment == path_segments[i]:
start_segment_index += 1
else:
break


pruned_path = os.sep.join(path_segments[start_segment_index:])
sparse_checkout(options, client, repo_url, pruned_path, local_checkout_root)


# =============================================================================
if __name__ == "__main__":


from optparse import OptionParser
usage = """%prog  [path2] [more paths...]"""


default_repo_url = "http://svn.example.com/MyRepository"
default_checkout_path = "sparse_trunk"


parser = OptionParser(usage)
parser.add_option("-r", "--repo_url", type="str", default=default_repo_url, dest="repo_url", help='Repository URL (default: "%s")' % default_repo_url)
parser.add_option("-l", "--local_path", type="str", default=default_checkout_path, dest="local_path", help='Local checkout path (default: "%s")' % default_checkout_path)


default_username = getpass.getuser()
parser.add_option("-u", "--username", type="str", default=default_username, dest="svn_username", help='SVN login username (default: "%s")' % default_username)
parser.add_option("-p", "--password", type="str", dest="svn_password", help="SVN login password")


parser.add_option("-v", "--verbose", action="store_true", default=False, dest="verbose", help="Verbose output")
(options, args) = parser.parse_args()


client = getSvnClient(options)
group_sparse_checkout(
options,
client,
options.repo_url,
map(os.path.relpath, args),
options.local_path)

如果已经有完整的本地副本,可以使用 --set-depth命令删除不需要的子文件夹。

svn update --set-depth=exclude www

见: http://blogs.collab.net/subversion/sparse-directories-now-with-exclusion

set-depth命令支持多重路径。

更新根本地副本不会更改已修改文件夹的深度。

要将文件夹恢复为递归签出,可以再次使用 --set-depth和无穷参数。

svn update --set-depth=infinity www

我为那些使用 TortoiseSvn 工具的人添加了这些信息: 为了获得 OP 相同的功能,你可以使用 Checkout 函数的 检查深度部分中的 Choose items...按钮,如下面的截图所示:

Images