Can you do a partial checkout with Subversion?

SvnSparse Checkout

Svn Problem Overview


If I had 20 directories under trunk/ with lots of files in each and only needed 3 of those directories, would it be possible to do a Subversion checkout with only those 3 directories under trunk?

Svn Solutions


Solution 1 - Svn

Indeed, thanks to the comments to my post here, it looks like sparse directories are the way to go. I believe the following should do it:

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

Alternatively, --depth immediates instead of empty checks out files and directories in trunk/proj without their contents. That way you can see which directories exist in the repository.


As mentioned in @zigdon's answer, you can also do a non-recursive checkout. This is an older and less flexible way to achieve a similar effect:

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

Solution 2 - Svn

Subversion 1.5 introduces sparse checkouts which may be something you might find useful. From the documentation:

> ... sparse directories (or shallow checkouts) ... allows you to easily check out a working copy—or a portion of a working copy—more shallowly than full recursion, with the freedom to bring in previously ignored files and subdirectories at a later time.

Solution 3 - Svn

I wrote a script to automate complex sparse checkouts.

#!/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:




Checkout the common root directory
Update with depth=empty for intermediate directories
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 <path1> [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)


Solution 4 - Svn

Or do a non-recursive checkout of /trunk, then just do a manual update on the 3 directories you need.

Solution 5 - Svn

If you already have the full local copy, you can remove unwanted sub folders by using --set-depth command.

svn update --set-depth=exclude www

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

The set-depth command support multipile paths.

Updating the root local copy will not change the depth of the modified folder.

To restore the folder to being recusively checkingout, you could use --set-depth again with infinity param.

svn update --set-depth=infinity www

Solution 6 - Svn

Sort of. As Bobby says:

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

will get the folders, but you will get separate folders from a subversion perspective. You will have to go separate commits and updates on each subfolder.

I don't believe you can checkout a partial tree and then work with the partial tree as a single entity.

Solution 7 - Svn

Not in any especially useful way, no. You can check out subtrees (as in Bobby Jack's suggestion), but then you lose the ability to update/commit them atomically; to do that, they need to be placed under their common parent, and as soon as you check out the common parent, you'll download everything under that parent. Non-recursive isn't a good option, because you want updates and commits to be recursive.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionreadonlyView Question on Stackoverflow
Solution 1 - SvnpkaedingView Answer on Stackoverflow
Solution 2 - SvnRichard MorganView Answer on Stackoverflow
Solution 3 - SvnkostmoView Answer on Stackoverflow
Solution 4 - SvnzigdonView Answer on Stackoverflow
Solution 5 - SvnFeng WeiweiView Answer on Stackoverflow
Solution 6 - SvnRob WalkerView Answer on Stackoverflow
Solution 7 - SvnDrPizzaView Answer on Stackoverflow