Skip to content

Instantly share code, notes, and snippets.

@ecarreras
Created August 20, 2012 13:16
Show Gist options
  • Save ecarreras/3403903 to your computer and use it in GitHub Desktop.
Save ecarreras/3403903 to your computer and use it in GitHub Desktop.
Checking local and remote md5
*.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()

Cutools (a.k.a Check Upgrade Tools)

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.

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 <ecarreras@gmail.com>
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='ecarreras@gmail.com',
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'],
)
@ecarreras
Copy link
Author

It could be great, check only if local modifications are in remote modifications

@ecarreras
Copy link
Author

Done! know if the md5 is different we search for chunks in remote repo

@ecarreras
Copy link
Author

Use hashlib!

@ecarreras
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment