Welcome, guest | Sign In | My Account | Store | Cart

This script removes all traces of subversion files from a directory tree.

Python, 69 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#!/usr/bin/env python
import os
import os.path
import sys
import stat
import logging

def gen_feedback(data):
    while True:
        for pos in range(len(data)):
            yield data[pos]

def fix_read_only(fname):
    '''Removes read only attribute if file is read only'''
    fileattr = os.stat(fname)[0] 
    if (not fileattr & stat.S_IWRITE):
        logging.info('Fixing read only file: ' + fname)
        os.chmod(fname, stat.S_IWRITE)

def clear_dir(dirname):
    '''Deletes all files in the directory tree and then dirs'''
    for root, dirs, files in os.walk(dirname):
        for i in files:
            tmpname = os.path.join(root,i)
            logging.info('Deleting file: ' + tmpname)
            try:
                fix_read_only(tmpname)
                os.remove(tmpname)
            except Exception as e:
                logging.exception(e)
    for root, dirs, files in os.walk(dirname):
        for j in dirs:
            tmpname = os.path.join(root,j)
            logging.info('Deleting dir: ' + tmpname)
            try:
                fix_read_only(tmpname)
                os.rmdir(tmpname)
            except Exception as e:
                logging.exception(e)
    try:
        fix_read_only(dirname) 
        logging.info('Deleting dir:' + dirname)
        os.rmdir(dirname)
    except Exception as e:
        logging.exception(e)

if __name__ == '__main__':
    sys.stdout.write('SVN Directory Remover\n')
    logging.basicConfig(level=logging.DEBUG)
    if len(sys.argv) > 1:
        basedir = sys.argv[1]
        dirlist = []  
        fb = gen_feedback('\\|/-') 
        for root, dirs, files in os.walk(basedir):    
            for i in dirs:
                sys.stdout.write(next(fb) + '\r') 
                if i == '.svn':
                    tmpname = os.path.join(root,i)
                    logging.info('Found: ' + tmpname)
                    dirlist.append(tmpname)
        
        if len(dirlist):
            for i in dirlist:
                logging.info('Clearing dir:' + i)
                clear_dir(i)
        else:
            sys.stdout.write('No .svn directories found\n')
    else:
        sys.stdout.write('usage: {0} <dirname>\n')

I decided to stop using subversion and found it a pain to delete all the .svn directories. I created this script to remove all the excess files that subversion leaves in a directory tree.
I'm sure there are more elegant ways to accomplish this. I decided to use python as the solution so I could learn the language better. I'm sharing it because I've read several people looking to solve some of the same problems that I had to solve creating this script. Any comments or corrections are welcomed.

NOTE: Several changes have been made as recommended in comments. The script should now run on both 2.6 and 3.x versions of Python. Also, a more elegant solution seems to be available... http://code.activestate.com/recipes/576643/

12 comments

Charlie Clark 12 years, 2 months ago  # | flag

While you seem to have got the grasp of Python this site isn't really about others just improving your code. Mailing lists and forums are better for that.

A couple of notes:

  • do not use globals. Python's scoping means that functions can see anything defined at module level (if you can see functions, why not variables?) but variables are constants. Always pass any variables you want to modify in as parameters or wrap them in a class.

  • Python 3.x requires print become print() and older versions support this as well. In fact, in your case, using the Python's logging makes more sense.

  • Use os.path to normalise any path manipulation operations in a platform independent way.

Other than that: welcome to Python!

Gabriel Genellina 12 years, 2 months ago  # | flag

I agree that this is not the best place to ask for advice. Anyway, my 0.08 ARS:

show_feedback is a function that must keep state. Instead of those nasty globals, you may:

a) Define a class, with self.feedbackpos, self.feedbackchars...

b) Use a generator. A direct traslation:

def gen_feedback():
  feedbackpos = 0
  feedbackchars = ['\\','|','/','-']
  while True:
    if feedbackpos > 3:
        feedbackpos = 0
    print feedbackchars[feedbackpos],'\r',
    feedbackpos += 1
    yield

show_feedback = gen_feedback().next

c) A more concise generator:

from itertools import cycle
def gen_feedback():
  feedbackchars = ['\\','|','/','-']
  for char in cycle(feedbackchars):
    print char,'\r',
    yield

show_feedback = gen_feedback().next

In Python 3 you could use a closure as well (thanks to the nonlocal keyword).

Amos Anderson (author) 12 years, 2 months ago  # | flag

Sorry, I didn't mean to convey that I was looking for help or assistance in improving my coding skills. I just wanted to share for those who are looking for a similar solution.

Amos Anderson (author) 12 years, 2 months ago  # | flag

Thanks for the feedback. I'll look into improving my submission and revise it when I'm done.

Amos Anderson (author) 12 years, 2 months ago  # | flag

I used sys.stdout.write for anything that was direct user feedback and logging for anything to do with files. That way it can be modified to use a verbosity flag if one desired to do so. sys.stdout.write makes it work fine with both 2.6 and 3.1 and seems to be recommended by some over the use of print.

I created an generator (commented out) but because of the way each version of the language handles next I decided to go with a class. Both the generator and class can use different characters for feedback, define at initialization.

I use os.path.join for concatenation of paths as recommended.

Gabriel Genellina 12 years, 2 months ago  # | flag

Remember that next(i) works in 2.6 too (calls i.next() in that version and i.__next__() in 3.x) so you don't have to worry about this particular difference.

Alia Khouri 12 years, 2 months ago  # | flag

I had a similar requirement some time ago and came up with this recipe: http://code.activestate.com/recipes/576643/ which has some features which may be interesting to you (hint: not just for subversion residue... :-)

Amos Anderson (author) 12 years, 2 months ago  # | flag

Gabriel, I actually didn't know that. That will be very helpful. Thank you.

Alia, your code certainly is more useful. When I get time I'll download your script and study it a bit. I'll probably use your script a lot too. Thank you.

Benjamin Sergeant 12 years, 2 months ago  # | flag

The simple way to do that with subversion is using svn export /tmp/myproject from your project root folder

Amos Anderson (author) 12 years, 1 month ago  # | flag

I guess I didn't know enough about subversion to do that. Thank you. I'll remember that from now on though.

Amos Anderson (author) 12 years, 1 month ago  # | flag

I always find the most complicated way of doing things. At least I learned a couple things.

Éric Araujo 12 years, 1 month ago  # | flag

If you can’t use svn export, find will do the job: find -type d -name .svn -print0 | xargs -0 rm -rf