/
__init__.py
123 lines (105 loc) · 4.74 KB
/
__init__.py
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import os
import re
import json
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.utils import six
from waliki.models import Page
from waliki import settings
from sh import git, ErrorReturnCode
git = git.bake("--no-pager")
class Git(object):
__shared_state = {} # it's a Borg
def __init__(self):
self.__dict__ = self.__shared_state
from waliki.settings import WALIKI_DATA_DIR
self.content_dir = WALIKI_DATA_DIR
os.chdir(self.content_dir)
if not os.path.isdir(os.path.join(self.content_dir, '.git')):
git.init()
git.config("user.email", settings.WALIKI_COMMITTER_EMAIL)
git.config("user.name", settings.WALIKI_COMMITTER_NAME)
def commit(self, page, message='', author=None, parent=None):
path = page.path
kwargs = {}
if isinstance(author, User) and author.is_authenticated():
kwargs['author'] = u"%s <%s>" % (author.get_full_name() or author.username, author.email)
elif isinstance(author, six.string_types):
kwargs['author'] = author
try:
there_were_changes = parent and parent != self.last_version(page)
status = git.status('--porcelain', path).stdout.decode('utf8')[:2]
if parent and status != "UU":
git.stash()
git.checkout('--detach', parent)
try:
git.stash('pop')
except:
git.checkout('--theirs', path)
if status == 'UU':
# See http://stackoverflow.com/a/8062976/811740
kwargs['i'] = True
git.add(path)
git.commit(path, allow_empty_message=True, m=message, **kwargs)
last = self.last_version(page)
if parent and status != "UU":
git.checkout('master')
git.merge(last)
except ErrorReturnCode as e:
# TODO: make this more robust!
error = e.stdout.decode('utf8')
if 'CONFLICT' in error:
# For '-i' attribute see http://stackoverflow.com/q/5827944/811740
git.commit(path, allow_empty_message=True, m=_('Merged with conflict'), i=True, **kwargs)
raise Page.EditionConflict(_('Automatic merge failed. Please, fix the conflict and save the page.'))
else:
raise
return there_were_changes
def history(self, page):
GIT_COMMIT_FIELDS = ['commit', 'author', 'date', 'date_relative', 'message']
GIT_LOG_FORMAT = '%x1f'.join(['%h', '%an', '%ad', '%ar', '%s']) + '%x1e'
output = git.log('--format=%s' % GIT_LOG_FORMAT, '-z', '--shortstat', page.abspath)
output = output.split('\n')
history = []
for line in output:
if '\x1f' in line:
log = line.strip('\x1e\x00').split('\x1f')
history.append(dict(zip(GIT_COMMIT_FIELDS, log)))
else:
insertion = re.match(r'.* (\d+) insertion', line)
deletion = re.match(r'.* (\d+) deletion', line)
history[-1]['insertion'] = int(insertion.group(1)) if insertion else 0
history[-1]['deletion'] = int(deletion.group(1)) if deletion else 0
max_changes = float(max([(v['insertion'] + v['deletion']) for v in history])) or 1.0
for v in history:
v.update({'insertion_relative': (v['insertion'] / max_changes) * 100,
'deletion_relative': (v['deletion'] / max_changes) * 100})
return history
def version(self, page, version):
try:
return six.text_type(git.show('%s:%s' % (version, page.path)))
except:
return ''
def last_version(self, page):
try:
return six.text_type(git.log("--pretty=format:%h", "-n 1", page.path))
except ErrorReturnCode:
return None
def whatchanged(self):
GIT_LOG_FORMAT = '%x1f'.join(['%an', '%ae', '%h', '%s', '%ar'])
pages = []
raw_log = git.whatchanged("--pretty=format:%s" % GIT_LOG_FORMAT).stdout.decode('utf8')
logs = re.findall(r'((.*)\x1f(.*)\x1f(.*)\x1f(.*)\x1f(.*))?\n:.*\t(.*)', raw_log, flags=re.MULTILINE | re.UNICODE)
for log in logs:
if log[0]:
log = list(log[1:])
log[-1] = [log[-1]]
pages.append(list(log))
else:
pages[-1][-1].append(log[-1])
return pages
def pull(self, remote):
log = git.pull('-s', 'recursive', '-X', 'ours', remote, 'HEAD').stdout.decode('utf8')
return log
def diff(self, page, new, old):
return git.diff('--no-color', new, old, '--', page.path).stdout.decode('utf8')