| 1 | # -*- Mode: Python; test-case-name: moap.test.test_vcs_darcs -*- |
|---|
| 2 | # vi:si:et:sw=4:sts=4:ts=4 |
|---|
| 3 | |
|---|
| 4 | """ |
|---|
| 5 | Darcs functionality. |
|---|
| 6 | """ |
|---|
| 7 | |
|---|
| 8 | import os |
|---|
| 9 | import commands |
|---|
| 10 | import re |
|---|
| 11 | |
|---|
| 12 | from moap.util import util, log |
|---|
| 13 | from moap.vcs import vcs |
|---|
| 14 | |
|---|
| 15 | def detect(path): |
|---|
| 16 | """ |
|---|
| 17 | Detect if the given source tree is using Darcs. |
|---|
| 18 | |
|---|
| 19 | @return: True if the given path looks like a Darcs tree. |
|---|
| 20 | """ |
|---|
| 21 | # for darcs, we may have to walk up directories |
|---|
| 22 | # see http://www.darcs.net/manual/node9.html |
|---|
| 23 | if not os.path.exists(os.path.join(path, '_darcs')): |
|---|
| 24 | log.debug('darcs', 'Did not find _darcs directory under %s' % path) |
|---|
| 25 | return False |
|---|
| 26 | |
|---|
| 27 | # some paths I found after an initialize and get |
|---|
| 28 | for n in ['inventory', 'inventories', 'checkpoints']: |
|---|
| 29 | if not os.path.exists(os.path.join(path, '_darcs', n)): |
|---|
| 30 | log.debug('darcs', 'Did not find _darcs/%s under %s' % (n, path)) |
|---|
| 31 | return False |
|---|
| 32 | |
|---|
| 33 | return True |
|---|
| 34 | |
|---|
| 35 | class Darcs(vcs.VCS): |
|---|
| 36 | name = 'Darcs' |
|---|
| 37 | |
|---|
| 38 | def getNotIgnored(self): |
|---|
| 39 | # darcs has "query manifest" to list version-controlled files, |
|---|
| 40 | # and a toplevel .darcs-boring file listing comments and regexps |
|---|
| 41 | oldPath = os.getcwd() |
|---|
| 42 | |
|---|
| 43 | versioned = [] |
|---|
| 44 | # output of this command starts with ./, filter that out |
|---|
| 45 | cmd = "darcs query manifest --repodir=%s" % self.path |
|---|
| 46 | output = commands.getoutput(cmd) |
|---|
| 47 | for line in output.split("\n"): |
|---|
| 48 | versioned.append(line[1 + len(os.path.sep):]) |
|---|
| 49 | self.debug('%d versioned files' % len(versioned)) |
|---|
| 50 | |
|---|
| 51 | allFiles = [] |
|---|
| 52 | |
|---|
| 53 | def walker(allFiles, dirname, fnames): |
|---|
| 54 | if not dirname.startswith(self.path): |
|---|
| 55 | fnames = [] |
|---|
| 56 | return |
|---|
| 57 | |
|---|
| 58 | reldirname = dirname[len(os.path.join(self.path, '')):] |
|---|
| 59 | # copy fnames so we can remove from it safely |
|---|
| 60 | for fname in fnames[:]: |
|---|
| 61 | # ignore _darcs directory in toplevel |
|---|
| 62 | if dirname == self.path and fname == "_darcs": |
|---|
| 63 | fnames.remove(fname) |
|---|
| 64 | continue |
|---|
| 65 | # darcs doesn't list directories as part of manifest |
|---|
| 66 | if not os.path.isdir(os.path.join(dirname, fname)): |
|---|
| 67 | allFiles.append(os.path.join(reldirname, fname)) |
|---|
| 68 | |
|---|
| 69 | os.path.walk(self.path, walker, allFiles) |
|---|
| 70 | self.debug('%d total files' % len(allFiles)) |
|---|
| 71 | |
|---|
| 72 | boringPath = os.path.join(self.path, '.darcs-boring') |
|---|
| 73 | boringRegExps = [] |
|---|
| 74 | if os.path.exists(boringPath): |
|---|
| 75 | boringRegExps = open(boringPath).readlines() |
|---|
| 76 | |
|---|
| 77 | unversioned = [n for n in allFiles if n not in versioned] |
|---|
| 78 | self.debug('%d unversioned files' % len(unversioned)) |
|---|
| 79 | |
|---|
| 80 | # now filter out based on the regexps |
|---|
| 81 | boring = [] |
|---|
| 82 | for exp in boringRegExps: |
|---|
| 83 | exp = exp.rstrip() |
|---|
| 84 | if not exp: |
|---|
| 85 | continue |
|---|
| 86 | if exp.startswith('#'): |
|---|
| 87 | continue |
|---|
| 88 | matcher = re.compile(exp) |
|---|
| 89 | for name in unversioned: |
|---|
| 90 | if matcher.match(name): |
|---|
| 91 | boring.append(name) |
|---|
| 92 | |
|---|
| 93 | unignored = [n for n in unversioned if n not in boring] |
|---|
| 94 | self.debug('%d unignored files' % len(unignored)) |
|---|
| 95 | |
|---|
| 96 | os.chdir(oldPath) |
|---|
| 97 | return unignored |
|---|
| 98 | |
|---|
| 99 | def ignore(self, paths, commit=True): |
|---|
| 100 | if not paths: |
|---|
| 101 | return |
|---|
| 102 | # FIXME: darcs allows regexp style, maybe our ignore should too |
|---|
| 103 | self.debug('ignoring %d paths' % len(paths)) |
|---|
| 104 | oldPath = os.getcwd() |
|---|
| 105 | os.chdir(self.path) |
|---|
| 106 | |
|---|
| 107 | boring = os.path.join(self.path, '.darcs-boring') |
|---|
| 108 | addBoring = not os.path.exists(boring) |
|---|
| 109 | handle = open(boring, "a") |
|---|
| 110 | self.debug('adding %d paths to %s' % (len(paths), boring)) |
|---|
| 111 | for path in paths: |
|---|
| 112 | handle.write('%s\n' % path) |
|---|
| 113 | handle.close() |
|---|
| 114 | |
|---|
| 115 | # FIXME: we should check if it is tracked, not if it exists; |
|---|
| 116 | # someone can have created it without adding it |
|---|
| 117 | if addBoring: |
|---|
| 118 | cmd = "darcs add .darcs-boring" |
|---|
| 119 | self.debug('Executing %s' % cmd) |
|---|
| 120 | os.system(cmd) |
|---|
| 121 | if commit: |
|---|
| 122 | cmd = "darcs record -am 'moap ignore' .darcs-boring" |
|---|
| 123 | self.debug('Executing %s' % cmd) |
|---|
| 124 | os.system(cmd) |
|---|
| 125 | |
|---|
| 126 | os.chdir(oldPath) |
|---|
| 127 | |
|---|
| 128 | def commit(self, paths, message): |
|---|
| 129 | # again, to commit, we need to be in the working directory |
|---|
| 130 | # paths are absolute, so scrub them |
|---|
| 131 | oldPath = os.getcwd() |
|---|
| 132 | os.chdir(self.path) |
|---|
| 133 | |
|---|
| 134 | temp = util.writeTemp([message, ]) |
|---|
| 135 | cmd = "darcs record -a --logfile=%s %s" % ( |
|---|
| 136 | temp, " ".join(paths)) |
|---|
| 137 | self.debug('running %s' % cmd) |
|---|
| 138 | status = os.system(cmd) |
|---|
| 139 | os.unlink(temp) |
|---|
| 140 | |
|---|
| 141 | os.chdir(oldPath) |
|---|
| 142 | |
|---|
| 143 | if status != 0: |
|---|
| 144 | return False |
|---|
| 145 | |
|---|
| 146 | return True |
|---|
| 147 | |
|---|
| 148 | def diff(self, path): |
|---|
| 149 | # darcs diff only works when in the working directory |
|---|
| 150 | oldPath = os.getcwd() |
|---|
| 151 | os.chdir(path) |
|---|
| 152 | |
|---|
| 153 | cmd = "darcs diff --unified" |
|---|
| 154 | self.debug('Running %s' % cmd) |
|---|
| 155 | output = commands.getoutput(cmd) |
|---|
| 156 | |
|---|
| 157 | os.chdir(oldPath) |
|---|
| 158 | return output |
|---|
| 159 | |
|---|
| 160 | def getFileMatcher(self): |
|---|
| 161 | return re.compile('^diff -rN -u old-.*/(\S+)$') |
|---|
| 162 | |
|---|
| 163 | def update(self, path): |
|---|
| 164 | # FIXME: I don't see a way to pull just updates to the given path |
|---|
| 165 | cmd = "darcs pull %s" % self.path |
|---|
| 166 | status, output = commands.getstatusoutput(cmd) |
|---|
| 167 | code = os.WEXITSTATUS(status) |
|---|
| 168 | if code not in (0, 2): |
|---|
| 169 | # darcs pull returns error code 2 when |
|---|
| 170 | # darcs failed: No default repository to pull from, please specify |
|---|
| 171 | # one |
|---|
| 172 | # we let it pass |
|---|
| 173 | raise vcs.VCSException(output) |
|---|
| 174 | |
|---|
| 175 | return output |
|---|
| 176 | |
|---|
| 177 | VCSClass = Darcs |
|---|