~brz/brz-debian/byoci

393.4.1 by Scott James Remnant
Bring in Scott James Remnant's merge_changelog script.
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
# Copyright ? 2008 Canonical Ltd.
4
# Author: Scott James Remnant <scott@ubuntu.com>.
5
# Hacked up by: Bryce Harrington <bryce@ubuntu.com>
6
#
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of version 3 of the GNU General Public License as
9
# published by the Free Software Foundation.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
750 by Jelmer Vernooij
use relative imports.
19
from __future__ import absolute_import
20
632.1.3 by Jelmer Vernooij
Cope with dpkg-mergechangelogs not being available.
21
import errno
581.3.3 by Andrew Bennetts
Update tests for different (but mostly better) behaviour provided by using dpkg-mergechangelogs.
22
import logging
581.3.1 by Andrew Bennetts
Quick hack to use dpkg-mergechangelogs.
23
import os.path
581.3.4 by Andrew Bennetts
Workaround bug 815700 in dpkg-mergechangelogs (it emits slightly non-standard conflict markers), and link to bug 815704 in an XXX comment in a test.
24
import re
581.3.1 by Andrew Bennetts
Quick hack to use dpkg-mergechangelogs.
25
import shutil
26
import subprocess
27
import tempfile
393.4.1 by Scott James Remnant
Bring in Scott James Remnant's merge_changelog script.
28
750 by Jelmer Vernooij
use relative imports.
29
from ... import (
393.4.14 by Vincent Ladeuil
Use the shiny merge.ConfigurableFileMerger.
30
    merge,
660.1.1 by Martin Packman
Fix handling of encoding with logging in merge_changelogs and tests
31
    osutils,
393.4.14 by Vincent Ladeuil
Use the shiny merge.ConfigurableFileMerger.
32
    )
33
400.2.1 by John Arbash Meinel
Lots of simplification.
34
581.3.3 by Andrew Bennetts
Update tests for different (but mostly better) behaviour provided by using dpkg-mergechangelogs.
35
# A logger in the 'bzr' hierarchy.  By default messages will be propagated to
36
# the standard bzr logger, but tests can easily intercept just this logger if
37
# they wish.
761 by Jelmer Vernooij
Fix remaining tests.
38
_logger = logging.getLogger('breezy.plugins.debian.merge_changelog')
581.3.3 by Andrew Bennetts
Update tests for different (but mostly better) behaviour provided by using dpkg-mergechangelogs.
39
40
393.4.14 by Vincent Ladeuil
Use the shiny merge.ConfigurableFileMerger.
41
class ChangeLogFileMerge(merge.ConfigurableFileMerger):
42
43
    name_prefix = 'deb_changelog'
44
    default_files = ['debian/changelog']
45
46
    def merge_text(self, params):
400.2.6 by John Arbash Meinel
Trying to trigger the invalid code, only to find bugs in python-debian :(
47
        return merge_changelog(params.this_lines, params.other_lines,
581.3.1 by Andrew Bennetts
Quick hack to use dpkg-mergechangelogs.
48
            params.base_lines)
49
393.4.1 by Scott James Remnant
Bring in Scott James Remnant's merge_changelog script.
50
400.2.2 by John Arbash Meinel
Make sure that the blocks are in sorted order before we do anything else.
51
def merge_changelog(this_lines, other_lines, base_lines=[]):
393.4.1 by Scott James Remnant
Bring in Scott James Remnant's merge_changelog script.
52
    """Merge a changelog file."""
581.3.1 by Andrew Bennetts
Quick hack to use dpkg-mergechangelogs.
53
    # Write the BASE, THIS and OTHER versions to files in a temporary
54
    # directory, and use dpkg-mergechangelogs to merge them.
55
    tmpdir = tempfile.mkdtemp('deb_changelog_merge')
56
    try:
57
        def writelines(filename, lines):
58
            with open(filename, 'w') as f:
59
                for line in lines:
60
                    f.write(line)
61
        base_filename = os.path.join(tmpdir, 'changelog.base')
62
        this_filename = os.path.join(tmpdir, 'changelog.this')
63
        other_filename = os.path.join(tmpdir, 'changelog.other')
64
        writelines(base_filename, base_lines)
65
        writelines(this_filename, this_lines)
66
        writelines(other_filename, other_lines)
632.1.3 by Jelmer Vernooij
Cope with dpkg-mergechangelogs not being available.
67
        try:
68
            proc = subprocess.Popen(['dpkg-mergechangelogs', base_filename,
69
                this_filename, other_filename], stdout=subprocess.PIPE,
70
                stderr=subprocess.PIPE)
71
        except OSError, e:
72
            if e.errno == errno.ENOENT:
73
                # No dpkg-mergechangelogs command available
638 by Vincent Ladeuil
Fix the returned values when dpkg-mergechangelogs is not available.
74
                return 'not_applicable', ''
632.1.3 by Jelmer Vernooij
Cope with dpkg-mergechangelogs not being available.
75
            raise
581.3.1 by Andrew Bennetts
Quick hack to use dpkg-mergechangelogs.
76
        stdout, stderr = proc.communicate()
77
        retcode = proc.returncode
581.3.3 by Andrew Bennetts
Update tests for different (but mostly better) behaviour provided by using dpkg-mergechangelogs.
78
        if stderr:
79
            # Relay the warning from dpkg-mergechangelogs to the user.  We
80
            # don't decorate the messages at all, as dpkg-mergechangelogs
81
            # warnings are already prefixed with "dpkg-mergechangelogs:
82
            # warning:" which makes the origin of the messages quite clear.
660.1.1 by Martin Packman
Fix handling of encoding with logging in merge_changelogs and tests
83
            encoding = osutils.get_user_encoding()
84
            # Errors are output using the locale, and log needs unicode.
85
            _logger.warning('%s', stderr.decode(encoding, "replace"))
581.3.1 by Andrew Bennetts
Quick hack to use dpkg-mergechangelogs.
86
        if retcode == 1:
581.3.4 by Andrew Bennetts
Workaround bug 815700 in dpkg-mergechangelogs (it emits slightly non-standard conflict markers), and link to bug 815704 in an XXX comment in a test.
87
            # dpkg-mergechangelogs reports a conflict.  Unfortunately it uses
88
            # slightly non-standard conflict markers (<http://pad.lv/815700>:
89
            # "<<<<<<" rather than "<<<<<<<", i.e. 6 chars instead of 7), so we
90
            # correct that here to make the results of this plugin as
91
            # consistent with regular bzr usage as possible.  Note that
92
            # conflict markers are never valid lines in a changelog file, so
93
            # it's reasonable for us to assume that any line that looks like a
94
            # conflict marker is a conflict marker (rather than valid content).
95
            # At worst a conflicted merge of an invalid changelog file that
96
            # already contained a non-standard conflict marker will have that
97
            # conflict marker made standard, which is more like a feature than
98
            # a bug!
99
            def replace_func(match_obj):
100
                match_text = match_obj.group(0)
101
                return match_text[0] * 7
604 by Jelmer Vernooij
Fix compatibility with python < 2.7, where re.sub() does not take a flags argument.
102
            stdout = re.sub('(?m)^[<=>]{6}$', replace_func, stdout)
581.3.1 by Andrew Bennetts
Quick hack to use dpkg-mergechangelogs.
103
            return 'conflicted', stdout
660.1.3 by Martin Packman
Make non-zero return from dpkg-mergechangelog fall back to other merge handlers
104
        elif retcode != 0:
105
            # dpkg-mergechangelogs exited with an error. There is probably no
106
            # output at all, but regardless the merge should fall back to
107
            # another method.
108
            _logger.warning("dpkg-mergechangelogs failed with status %d", retcode)
109
            return 'not_applicable', stdout
400.2.3 by John Arbash Meinel
Fix bug #516060, implement 3-way changelog merge.
110
        else:
581.3.1 by Andrew Bennetts
Quick hack to use dpkg-mergechangelogs.
111
            return 'success', stdout
112
    finally:
113
        shutil.rmtree(tmpdir)