def _build_line_mappings(self, start, finish):
        forward = {}
        backward = {}

        # Get information about blank lines: The git diff porcelain format
        # (which we use for everything else) doesn't distinguish between
        # additions and removals, so this is a very dirty hack to get around
        # the problem.
        p = os.popen('git diff %s %s -- %s | grep -E "^[+-]$"' % (
            start,
            finish,
            self.path,
        ))
        blank_lines = [l.strip() for l in p.readlines()]

        p = os.popen('git diff --word-diff=porcelain %s %s -- %s' % (
            start,
            finish,
            self.path,
        ))

        # The diff output is in sections: A header line (indicating the
        # range of lines this section covers) and then a number of
        # content lines.

        sections = []

        # Skip initial headers: They don't interest us.
        line = ''
        while not line.startswith('@@'):
            line = p.readline()

        while line:
            header_line = line
            content_lines = []

            line = p.readline()
            while line and not line.startswith('@@'):
                content_lines.append(line)
                line = p.readline()

            sections.append((header_line, content_lines, ))


        start_ln = finish_ln = 0
        for header_line, content_lines in sections:
            # The headers line has the format '@@ +a,b -c,d @@[ e]' where
            # a is the first line number shown from start and b is the
            # number of lines shown from start, and c is the first line
            # number show from finish and d is the number of lines show
            # from from finish, and e is Git's guess at the name of the
            # context (and is not always present)

            headers = header_line.strip('@ \n').split(' ')
            headers = map(lambda x: x.strip('+-').split(','), headers)

            start_range = map(int, headers[0])
            finish_range = map(int, headers[1])

            while start_ln < start_range[0] - 1 and \
                  finish_ln < finish_range[0] - 1:
                forward[start_ln] = finish_ln
                backward[finish_ln] = start_ln
                start_ln += 1
                finish_ln += 1

            # Now we're into the diff itself. Individual lines of input
            # are separated by a line containing only a '~', this helps
            # to distinguish between an addition, a removal, and a change.

            line_iter = iter(content_lines)
            try:
                while True:
                    group_size = -1
                    line_delta = 0
                    line = ' '
                    while line != '~':
                        if line.startswith('+'):
                            line_delta += 1
                        elif line.startswith('-'):
                            line_delta -= 1

                        group_size += 1
                        line = line_iter.next().rstrip()

                    if group_size == 0:
                        # Two '~' lines next to each other means a blank
                        # line has been either added or removed. Git
                        # doesn't tell us which. This is all crazy.
                        if blank_lines.pop(0) == '+':
                            line_delta += 1
                        else:
                            line_delta -= 1

                    if line_delta == 1:
                        backward[finish_ln] = None
                        finish_ln += 1
                    elif line_delta == -1:
                        forward[start_ln] = None
                        start_ln += 1
                    else:
                        forward[start_ln] = finish_ln
                        backward[finish_ln] = start_ln
                        start_ln += 1
                        finish_ln += 1
            except __HOLE__:
                pass

        # Make sure the mappings stretch the the beginning and end of
        # the files.

        p = os.popen('git show %s:%s' % (start, self.path))
        start_len = len(p.readlines())

        p = os.popen('git show %s:%s' % (finish, self.path))
        finish_len = len(p.readlines())

        while start_ln <= start_len and finish_ln <= finish_len:
            forward[start_ln] = finish_ln
            backward[finish_ln] = start_ln
            start_ln += 1
            finish_ln += 1

        return forward, backward
dataset/ETHPy150Open georgebrock/git-browse/gitbrowse/git.py/GitFileHistory._build_line_mappings