def diff(request): if not request.GET.get('repo'): return http.HttpResponseBadRequest("Missing 'repo' parameter") reponame = request.GET['repo'] repopath = settings.REPOSITORY_BASE + '/' + reponame try: repo_url = Repository.objects.get(name=reponame).url except Repository.DoesNotExist: raise http.Http404("Repository not found") if not request.GET.get('from'): return http.HttpResponseBadRequest("Missing 'from' parameter") if not request.GET.get('to'): return http.HttpResponseBadRequest("Missing 'to' parameter") ui = _ui() repo = repository(ui, repopath) # Convert the 'from' and 'to' to strings (instead of unicode) # in case mercurial needs to look for the key in binary data. # This prevents UnicodeWarning messages. ctx1 = repo.changectx(str(request.GET['from'])) ctx2 = repo.changectx(str(request.GET['to'])) copies = pathcopies(ctx1, ctx2) match = None # maybe get something from l10n.ini and cmdutil changed, added, removed = repo.status(ctx1, ctx2, match=match)[:3] # split up the copies info into thos that were renames and those that # were copied. moved = {} copied = {} for new_name, old_name in copies.items(): if old_name in removed: moved[new_name] = old_name else: copied[new_name] = old_name paths = ([(f, 'changed') for f in changed] + [(f, 'removed') for f in removed if f not in moved.values()] + [(f, (f in moved and 'moved') or (f in copied and 'copied') or 'added') for f in added]) diffs = DataTree(dict) for path, action in paths: lines = [] try: p = getParser(path) except UserWarning: diffs[path].update({ 'path': path, 'isFile': True, 'rev': ((action == 'removed') and request.GET['from'] or request.GET['to']), 'class': action, 'renamed': moved.get(path), 'copied': copied.get(path) }) continue if action == 'added': a_entities = [] a_map = {} else: realpath = (action == 'moved' and moved[path] or action == 'copied' and copied[path] or path) data = ctx1.filectx(realpath).data() data = _universal_newlines(data) try: p.readContents(data) a_entities, a_map = p.parse() except: # consider doing something like: # logging.warn('Unable to parse %s', path, exc_info=True) diffs[path].update({ 'path': path, 'isFile': True, 'rev': ((action == 'removed') and request.GET['from'] or request.GET['to']), 'class': action, 'renamed': moved.get(path), 'copied': copied.get(path) }) continue if action == 'removed': c_entities, c_map = [], {} else: data = ctx2.filectx(path).data() data = _universal_newlines(data) try: p.readContents(data) c_entities, c_map = p.parse() except: # consider doing something like: # logging.warn('Unable to parse %s', path, exc_info=True) diffs[path].update({ 'path': path, 'isFile': True, 'rev': ((action == 'removed') and request.GET['from'] or request.GET['to']), 'class': action }) continue a_list = sorted(a_map.keys()) c_list = sorted(c_map.keys()) ar = AddRemove() ar.set_left(a_list) ar.set_right(c_list) for action, item_or_pair in ar: if action == 'delete': lines.append({ 'class': 'removed', 'oldval': [{'value':a_entities[a_map[item_or_pair]].val}], 'newval': '', 'entity': item_or_pair }) elif action == 'add': lines.append({ 'class': 'added', 'oldval': '', 'newval': [{'value': c_entities[c_map[item_or_pair]].val}], 'entity': item_or_pair }) else: oldval = a_entities[a_map[item_or_pair[0]]].val newval = c_entities[c_map[item_or_pair[1]]].val if oldval == newval: continue sm = SequenceMatcher(None, oldval, newval) oldhtml = [] newhtml = [] for op, o1, o2, n1, n2 in sm.get_opcodes(): if o1 != o2: oldhtml.append({'class': op, 'value': oldval[o1:o2]}) if n1 != n2: newhtml.append({'class': op, 'value': newval[n1:n2]}) lines.append({'class': 'changed', 'oldval': oldhtml, 'newval': newhtml, 'entity': item_or_pair[0]}) container_class = lines and 'file' or 'empty-diff' diffs[path].update({'path': path, 'class': container_class, 'lines': lines, 'renamed': moved.get(path), 'copied': copied.get(path) }) diffs = diffs.toJSON().get('children', []) return render(request, 'shipping/diff.html', { 'given_title': request.GET.get('title', None), 'repo': reponame, 'repo_url': repo_url, 'old_rev': request.GET['from'], 'new_rev': request.GET['to'], 'diffs': diffs })
def diff_app(request): # XXX TODO: error handling if 'repo' not in request.GET: raise Http404("repo must be supplied") reponame = request.GET['repo'] repopath = settings.REPOSITORY_BASE + '/' + reponame repo_url = Repository.objects.filter(name=reponame).values_list('url', flat=True)[0] from mercurial.ui import ui as _ui from mercurial.hg import repository ui = _ui() repo = repository(ui, repopath) ctx1 = repo.changectx(request.GET['from']) ctx2 = repo.changectx(request.GET['to']) match = None # maybe get something from l10n.ini and cmdutil changed, added, removed = repo.status(ctx1, ctx2, match=match)[:3] paths = ([(f, 'changed') for f in changed] + [(f, 'removed') for f in removed] + [(f, 'added') for f in added]) diffs = DataTree(dict) for path, action in paths: lines = [] try: p = getParser(path) except UserWarning: diffs[path].update({'path': path, 'isFile': True, 'rev': ((action == 'removed') and request.GET['from'] or request.GET['to']), 'class': action}) continue if action == 'added': a_entities = [] a_map = {} else: data = ctx1.filectx(path).data() data = _universal_newlines(data) try: p.readContents(data) a_entities, a_map = p.parse() except: diffs[path].update({'path': path, 'isFile': True, 'rev': ((action == 'removed') and request.GET['from'] or request.GET['to']), 'class': action}) continue if action == 'removed': c_entities = [] c_map = {} else: data = ctx2.filectx(path).data() data = _universal_newlines(data) try: p.readContents(data) c_entities, c_map = p.parse() except: diffs[path].update({'path': path, 'isFile': True, 'rev': ((action == 'removed') and request.GET['from'] or request.GET['to']), 'class': action}) continue a_list = sorted(a_map.keys()) c_list = sorted(c_map.keys()) ar = AddRemove() ar.set_left(a_list) ar.set_right(c_list) for action, item_or_pair in ar: if action == 'delete': lines.append({'class': 'removed', 'oldval': [{'value':a_entities[a_map[item_or_pair]].val}], 'newval': '', 'entity': item_or_pair}) elif action == 'add': lines.append({'class': 'added', 'oldval': '', 'newval':[{'value': c_entities[c_map[item_or_pair]].val}], 'entity': item_or_pair}) else: oldval = a_entities[a_map[item_or_pair[0]]].val newval = c_entities[c_map[item_or_pair[1]]].val if oldval == newval: continue sm = SequenceMatcher(None, oldval, newval) oldhtml = [] newhtml = [] for op, o1, o2, n1, n2 in sm.get_opcodes(): if o1 != o2: oldhtml.append({'class':op, 'value':oldval[o1:o2]}) if n1 != n2: newhtml.append({'class':op, 'value':newval[n1:n2]}) lines.append({'class':'changed', 'oldval': oldhtml, 'newval': newhtml, 'entity': item_or_pair[0]}) container_class = lines and 'file' or 'empty-diff' diffs[path].update({'path': path, 'class': container_class, 'lines': lines}) diffs = diffs.toJSON().get('children', []) return render_to_response('shipping/diff.html', {'given_title': request.GET.get('title', None), 'repo': reponame, 'repo_url': repo_url, 'old_rev': request.GET['from'], 'new_rev': request.GET['to'], 'diffs': diffs}, context_instance=RequestContext(request))
def diff_app(request): # XXX TODO: error handling reponame = request.GET['repo'] repopath = settings.REPOSITORY_BASE + '/' + reponame repo_url = Repository.objects.filter(name=reponame).values_list('url', flat=True)[0] from mercurial.ui import ui as _ui from mercurial.hg import repository ui = _ui() repo = repository(ui, repopath) ctx1 = repo.changectx(request.GET['from']) ctx2 = repo.changectx(request.GET['to']) match = None # maybe get something from l10n.ini and cmdutil changed, added, removed = repo.status(ctx1, ctx2, match=match)[:3] diffs = DataTree(dict) for path in added: diffs[path].update({'path': path, 'isFile': True, 'rev': request.GET['to'], 'desc': ' (File added)', 'class': 'added'}) for path in removed: diffs[path].update({'path': path, 'isFile': True, 'rev': request.GET['from'], 'desc': ' (File removed)', 'class': 'removed'}) for path in changed: lines = [] try: p = getParser(path) except UserWarning: diffs[path].update({'path': path, 'lines': [{'class': 'issue', 'oldval': '', 'newval': '', 'entity': 'cannot parse ' + path}]}) continue data1 = ctx1.filectx(path).data() data2 = ctx2.filectx(path).data() try: # parsing errors or such can break this, catch those and fail # gracefully # fake reading with universal line endings, too p.readContents(__universal_le(data1)) a_entities, a_map = p.parse() p.readContents(__universal_le(data2)) c_entities, c_map = p.parse() del p except: diffs[path].update({'path': path, 'lines': [{'class': 'issue', 'oldval': '', 'newval': '', 'entity': 'cannot parse ' + path}]}) continue a_list = sorted(a_map.keys()) c_list = sorted(c_map.keys()) ar = AddRemove() ar.set_left(a_list) ar.set_right(c_list) for action, item_or_pair in ar: if action == 'delete': lines.append({'class': 'removed', 'oldval': [{'value':a_entities[a_map[item_or_pair]].val}], 'newval': '', 'entity': item_or_pair}) elif action == 'add': lines.append({'class': 'added', 'oldval': '', 'newval':[{'value': c_entities[c_map[item_or_pair]].val}], 'entity': item_or_pair}) else: oldval = a_entities[a_map[item_or_pair[0]]].val newval = c_entities[c_map[item_or_pair[1]]].val if oldval == newval: continue sm = SequenceMatcher(None, oldval, newval) oldhtml = [] newhtml = [] for op, o1, o2, n1, n2 in sm.get_opcodes(): if o1 != o2: oldhtml.append({'class':op, 'value':oldval[o1:o2]}) if n1 != n2: newhtml.append({'class':op, 'value':newval[n1:n2]}) lines.append({'class':'changed', 'oldval': oldhtml, 'newval': newhtml, 'entity': item_or_pair[0]}) container_class = lines and 'file' or 'empty-diff' diffs[path].update({'path': path, 'class': container_class, 'lines': lines}) diffs = diffs.toJSON().get('children', []) return render_to_response('shipping/diff.html', {'given_title': request.GET.get('title', None), 'repo': reponame, 'repo_url': repo_url, 'old_rev': request.GET['from'], 'new_rev': request.GET['to'], 'diffs': diffs})