For this moment only git is supported.
$ cugit check origin/master
This will check if local modifications will come when you merge origin/master in your local branch.
| *.py[cod] | |
| # C extensions | |
| *.so | |
| # Packages | |
| *.egg | |
| *.egg-info | |
| dist | |
| build | |
| eggs | |
| parts | |
| bin | |
| var | |
| sdist | |
| develop-eggs | |
| .installed.cfg | |
| lib | |
| lib64 | |
| # Installer logs | |
| pip-log.txt | |
| # Unit test / coverage reports | |
| .coverage | |
| .tox | |
| nosetests.xml | |
| #Translations | |
| *.mo | |
| #Mr Developer | |
| .mr.developer.cfg | |
| .project | |
| .pydevproject |
| try: | |
| VERSION = __import__('pkg_resources').get_distribution('cutools').version | |
| except Exception, e: | |
| VERSION = 'unknown' |
| from hashlib import md5 | |
| from clint.textui import puts, colored | |
| def clean_diff(diff): | |
| """Removes diff header from a diff. | |
| """ | |
| res = [] | |
| skip = True | |
| for line in diff.split('\n'): | |
| if line.startswith('diff --git'): | |
| skip = True | |
| if line.startswith('@@ '): | |
| skip = False | |
| if not skip: | |
| res.append(line) | |
| return '\n'.join(res) | |
| def print_diff(diff): | |
| """Prints colored diff. | |
| """ | |
| for line in diff.split('\n'): | |
| line = unicode(line).encode('utf-8') | |
| if line.startswith('+'): | |
| puts(colored.green(line)) | |
| elif line.startswith('-'): | |
| puts(colored.red(line)) | |
| else: | |
| puts(line) | |
| def get_chunks(diff): | |
| """Returns a list with all the chunks in this diff. | |
| """ | |
| diff = clean_diff(diff) | |
| chunk = [] | |
| chunks = [] | |
| for line in diff.split('\n'): | |
| if not line: | |
| continue | |
| if line.startswith('@@ '): | |
| if chunk: | |
| chunks.append('\n'.join(chunk)) | |
| chunk = [line] | |
| else: | |
| chunk.append(line) | |
| if chunk: | |
| chunks.append('\n'.join(chunk)) | |
| return chunks | |
| def get_hashed_chunks(chunks): | |
| chunks_dict = {} | |
| for chunk in chunks: | |
| chunks_dict[md5(unicode(chunk).encode('utf-8')).hexdigest()] = chunk | |
| return chunks_dict | |
| def clean_chunk(chunk): | |
| """Clean headers from chunk. | |
| """ | |
| return '\n'.join([x[1:] for x in chunk.split('\n') | |
| if x and x[0] not in ('-', '@')]) | |
| def chunk_in_text(chunk, text): | |
| """Checks if chunk is inside text. | |
| """ | |
| chunk = clean_chunk(chunk) | |
| return text.find(chunk) >= 0 |
| from collections import defaultdict | |
| class VCSInterface(object): | |
| """Interface to create VCS objects to perform an upgrade check. | |
| """ | |
| def __init__(self, upstrem): | |
| """Sets the upstream to perform the upgrade check. | |
| """ | |
| raise NotImplementedError() | |
| def get_md5_files(self): | |
| """Must return a list of list of tuples with (md5, filename) modified | |
| in working directory. | |
| [('43f6e228690472109d1c825bdcd1625b', 'README.rst), | |
| ('3af3d58716ec0776500dc970020a5100', 'src/foo.py)] | |
| """ | |
| raise NotImplementedError() | |
| @property | |
| def local_rev(self): | |
| """Returns local revision of HEAD. | |
| """ | |
| raise NotImplementedError() | |
| @property | |
| def remote_rev(self): | |
| """Returns remote revision of HEAD | |
| """ | |
| raise NotImplementedError() | |
| def get_commits(self, check_file, rev_from, rev_to): | |
| """Returns a list of commits beetwen rev_from and rev_to for check_file | |
| """ | |
| raise NotImplementedError() | |
| def get_chunks(self, commit, check_file): | |
| """Returns the chunks from a commit. | |
| """ | |
| raise NotImplementedError() | |
| def get_remote_file(self, check_file): | |
| """Returns the content of the remote file | |
| """ | |
| raise NotImplementedError() |
| from hashlib import md5 | |
| from subcmd.app import App | |
| from subcmd.decorators import arg | |
| from cutools.vcs.git import Git | |
| from cutools.diff import get_hashed_chunks, clean_chunk, print_diff | |
| from cutools import VERSION | |
| from clint.textui import puts, colored | |
| class CuGitApp(App): | |
| name = "cugit" | |
| version = VERSION | |
| @arg('upstream', help='Upstream branch') | |
| def do_check(self, options): | |
| """Checks local modifcations if are in upstream. | |
| """ | |
| git = Git(options.upstream) | |
| n_files = 0 | |
| n_chunks = 0 | |
| for pymd5, check_file in git.get_md5_files(): | |
| if md5(open(check_file, 'r').read()).hexdigest() != pymd5: | |
| local_chunks = get_hashed_chunks(git.get_chunks(check_file)) | |
| rev_from = git.local_rev | |
| rev_to = git.remote_rev | |
| for commit in git.get_commits(check_file, rev_from, rev_to): | |
| remote_chunks = [ | |
| md5(unicode(x).encode('utf-8')).hexdigest() | |
| for x in git.get_chunks(check_file, commit) | |
| ] | |
| for lchunk in local_chunks.keys(): | |
| if lchunk in remote_chunks: | |
| del local_chunks[lchunk] | |
| else: | |
| rfile = git.get_remote_file(check_file) | |
| chunk = clean_chunk(local_chunks[lchunk]) | |
| if rfile.find(chunk) >= 0: | |
| del local_chunks[lchunk] | |
| if local_chunks: | |
| n_files += 1 | |
| for chunk in local_chunks.values(): | |
| print_diff(chunk) | |
| n_chunks += 1 | |
| puts(colored.red("[x] %s %s" % (pymd5, check_file))) | |
| else: | |
| puts(colored.green("[o] %s %s" % (pymd5, check_file))) | |
| app = CuGitApp() |
| from hashlib import md5 | |
| from cutools.vcs import VCSInterface | |
| from cutools.diff import get_chunks | |
| from plumbum.cmd import git | |
| class Git(VCSInterface): | |
| """Git implementation for Check Upgrade Tools. | |
| """ | |
| def __init__(self, upstream): | |
| self.upstream = upstream | |
| def get_md5_files(self): | |
| res = [] | |
| files = ['%s' % x for x in git['ls-files', '-m']().split('\n') if x] | |
| for fl in files: | |
| cmd = git['show', '%s:%s' % (self.upstream, fl)] | |
| pymd5 = md5(unicode(cmd()).encode('utf-8')).hexdigest() | |
| res += [(pymd5, fl)] | |
| return res | |
| @property | |
| def local_rev(self): | |
| return git['rev-parse', 'HEAD']().strip() | |
| @property | |
| def remote_rev(self): | |
| return git['rev-parse', self.upstream]().strip() | |
| def get_commits(self, check_file, rev_from, rev_to): | |
| hcommand = git['log', '--no-merges', '--pretty=oneline', | |
| '%s..%s' % (rev_from, rev_to), | |
| check_file] | |
| return [x.split(' ')[0] for x in hcommand().split('\n') if x] | |
| def get_chunks(self, check_file, commit=None): | |
| if commit: | |
| cmd = git['diff', '%s^1..%s'% (commit, commit), check_file] | |
| else: | |
| cmd = git['diff', check_file] | |
| return get_chunks( | |
| cmd() | |
| ) | |
| def get_remote_file(self, check_file): | |
| return git['show', '%s:%s' % (self.upstream, check_file)]() |
| Copyright (c) 2012, Eduard Carreras <[email protected]> | |
| Permission to use, copy, modify, and/or distribute this software for any | |
| purpose with or without fee is hereby granted, provided that the above | |
| copyright notice and this permission notice appear in all copies. | |
| THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
| from setuptools import setup, find_packages | |
| from cutools import VERSION | |
| def readfile(rfile): | |
| try: | |
| with open(rfile, 'w') as f: | |
| return f.read() | |
| except: | |
| return '' | |
| setup( | |
| name='cutools', | |
| version='0.1.0', | |
| description='Check local modifications in upstream branch', | |
| long_description=readfile('README.rst'), | |
| author='Eduard Carreras', | |
| author_email='[email protected]', | |
| url='https://github.com/ecarreras/cutools', | |
| license=readfile('LICENSE'), | |
| packages=find_packages(), | |
| classifiers=[ | |
| 'Development Status :: 4 - Beta', | |
| 'Environment :: Console', | |
| 'Intended Audience :: Developers', | |
| 'Intended Audience :: System Administrators', | |
| 'Programming Language :: Python', | |
| 'Programming Language :: Python :: 2', | |
| 'Programming Language :: Python :: 2.7', | |
| ], | |
| platforms=['Any'], | |
| scripts=[], | |
| entry_points={ | |
| 'console_scripts': [ | |
| 'cugit = cutools.cli.cugit:app.cmdline' | |
| ] | |
| }, | |
| provides=['cutools'], | |
| install_requires=['clint', 'plumbum', 'subcmd'], | |
| ) |
Done! know if the md5 is different we search for chunks in remote repo
Use hashlib!
Poder acabar fent un:
gitcu check: Et comprovi el que no tens en upstream i t'ho ensenyi
gitcu diff: Et generi un diff en el format de git per després poder-lo aplicar.
It could be great, check only if local modifications are in remote modifications