Puoi fare un checkout parziale con Subversion?
-
09-06-2019 - |
Domanda
Se avessi 20 directory nel trunk/ con molti file in ciascuna e avessi bisogno solo di 3 di quelle directory, sarebbe possibile eseguire un checkout Subversion con solo quelle 3 directory nel trunk?
Soluzione
La sottoversione 1.5 introduce checkout sparsi che potrebbero essere qualcosa che potresti trovare utile.Dal documentazione:
... directory sparse (O casse poco profonde) ...ti consente di estrarre facilmente una copia di lavoro, o una parte di una copia di lavoro, in modo più superficiale rispetto alla ricorsione completa, con la libertà di importare file e sottodirectory precedentemente ignorati in un secondo momento.
Altri suggerimenti
In effetti, grazie ai commenti al mio post qui, sembra directory sparse sono la strada da percorrere.Credo che quanto segue dovrebbe farlo:
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
In alternativa, --depth immediates
invece di empty
controlla i file e le directory in trunk/proj
senza il loro contenuto.In questo modo puoi vedere quali directory esistono nel repository.
Come menzionato nella risposta di @zigdon, puoi anche eseguire un checkout non ricorsivo.Questo è un modo più vecchio e meno flessibile per ottenere un effetto simile:
svn checkout --non-recursive http://svnserver/trunk/proj
svn update trunk/foo
svn update trunk/bar
svn update trunk/baz
Ho scritto uno script per automatizzare i checkout sparsi complessi.
#!/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)
Oppure esegui un checkout non ricorsivo di /trunk, quindi esegui semplicemente un aggiornamento manuale sulle 3 directory che ti servono.
Se disponi già della copia locale completa, puoi rimuovere le sottocartelle indesiderate utilizzando --set-depth
comando.
svn update --set-depth=exclude www
Vedere: http://blogs.collab.net/subversion/sparse-directories-now-with-exclusion
IL set-depth
comando supporta percorsi multipli.
L'aggiornamento della copia locale root non modificherà la profondità della cartella modificata.
Per ripristinare la cartella in fase di checkout ricorsivo, è possibile utilizzare --set-depth
ancora una volta con il parametro infinito.
svn update --set-depth=infinity www
Una specie di.Come dice Bobby:
svn co file:///.../trunk/foo file:///.../trunk/bar file:///.../trunk/hum
otterrà le cartelle, ma otterrai cartelle separate dal punto di vista di sovversione.Dovrai eseguire commit e aggiornamenti separati su ciascuna sottocartella.
Non credo che tu possa estrarre un albero parziale e poi lavorare con l'albero parziale come una singola entità.
Non in alcun modo particolarmente utile, no.Puoi controllare i sottoalberi (come nel suggerimento di Bobby Jack), ma poi perdi la possibilità di aggiornarli/impegnarli atomicamente;per fare ciò, devono essere posizionati sotto il genitore comune e non appena estrai il genitore comune, scaricherai tutto sotto quel genitore.La modalità non ricorsiva non è una buona opzione, perché si desidera che gli aggiornamenti e i commit siano ricorsivi.