Merge lp:~jameinel/bzr-builddeb/changelog-hook into lp:bzr-builddeb

Proposed by John A Meinel
Status: Merged
Merged at revision: not available
Proposed branch: lp:~jameinel/bzr-builddeb/changelog-hook
Merge into: lp:bzr-builddeb
Prerequisite: lp:~jameinel/bzr-builddeb/use_loader
Diff against target: 453 lines (+411/-2)
4 files modified
__init__.py (+20/-2)
merge_changelog.py (+262/-0)
tests/__init__.py (+1/-0)
tests/test_merge_changelog.py (+128/-0)
To merge this branch: bzr merge lp:~jameinel/bzr-builddeb/changelog-hook
Reviewer Review Type Date Requested Status
James Westby Approve
Review via email: mp+18192@code.launchpad.net
To post a comment you must log in.
Revision history for this message
John A Meinel (jameinel) wrote :

This adds a per-file commit hook, which checks
a) A Branch config called
  changelog_hook_enabled = True
b) A file named 'changelog' or 'debian/changelog'

If both hold true, and it perceives a conflict in the changelog file, it essentially runs Scott's merge_changelog code. This will always result in a conflict-free changelog file. The current algorithm is "prefer-mine". So if both changelogs have a section with a matching 'version', the text present in "this" are used.

So if a Ubuntu Developer wants to enable this plugin, they can edit branch.conf or locations.conf (or bazaar.conf) to set 'changelog_hook_enabled = True'. We can argue that just having 'bzr-builddeb' installed, would be sufficient to enable this for all branches when merging. (We could make the default True, and use the config to *disable* the plugin.)

There are some new tests for this, but I've also manually tested merging lp:ubuntu/bzrtools @-r4.3.1 into -r 50. And it seemed to do a nice clean merge (without the plugin enabled it conflicted as expected.) As an example:

 bzr branch lp:ubuntu/bzrtools -r 4.3.1 bzrtools_upstream
 bzr branch lp:ubuntu/bzrtools -r 50 bzrtools_ubuntu
 cd bzrtools_ubuntu
 bzr merge ../bzrtools_upstream

(There are other conflicts, but *debian/changelog* is not one of them)

Revision history for this message
Robert Collins (lifeless) wrote :

top level stuff:
lets call it deb_changelog_custom_merge = True. The fact its hook based isn't too relevant to users, and it uses debian logic to do the merges.

I don't know that merging 'changelog' is needed, as all the ubuntu package imports have debian/changelog, not 'changelog' as the filename.

I would make it enabled by default.

Finally, if we do do merges of 'changelog' we should check it /looks/ like a debian changelog and punt if it doesn't. Or use another heuristic like looking for a 'rules' file too.

Revision history for this message
Robert Collins (lifeless) wrote :

Lets namespace the params._changelog... to be _builddeb_changelog....

also please file a bug saying how ugly that is in bzr core - we should fix that for 2.2 for sure, IMO. Or perhaps even for 2.1?

Other than that, my earlier comments and Scotts code which I have gone la-la-la to, its good.

Revision history for this message
Robert Collins (lifeless) wrote :

Also as per discussion in rl, do deb_changelog_merge_files = [list defaulting to debian/changelog] as better way to control it.

405. By John A Meinel

Change over to using a list of filenames as the enablement.

Default to enabling the plugin for 'debian/changelog' only.

Revision history for this message
John A Meinel (jameinel) wrote :

Updated as recommended.

1) Plugin now defaults to active, but only handling 'debian/changelog'
2) Setting the config variable:
  deb_changelog_merge_files =
Empty will disable it, comma-separated values if you want to check multiple files (same as how news_merge does it.)
3) Changed variable naming, etc, as requested.

406. By Vincent Ladeuil

Get fixes from trunk

407. By Vincent Ladeuil

Use the shiny merge.ConfigurableFileMerger.

This requires bzr-2.1but makes it even easier to define the per-file
merge hooks.

Test updated accordingly.

408. By Vincent Ladeuil

Raise ImportError if we're not running at least bzr-2.1.0rc2.

Revision history for this message
James Westby (james-w) wrote :

> This adds a per-file commit hook, which checks
> a) A Branch config called
> changelog_hook_enabled = True
> b) A file named 'changelog' or 'debian/changelog'
>
> If both hold true, and it perceives a conflict in the changelog file, it
> essentially runs Scott's merge_changelog code. This will always result in a
> conflict-free changelog file. The current algorithm is "prefer-mine". So if
> both changelogs have a section with a matching 'version', the text present in
> "this" are used.

That sounds wrong to me, why wouldn't it conflict those blocks?

> There are some new tests for this, but I've also manually tested merging
> lp:ubuntu/bzrtools @-r4.3.1 into -r 50. And it seemed to do a nice clean merge
> (without the plugin enabled it conflicted as expected.) As an example:
>
> bzr branch lp:ubuntu/bzrtools -r 4.3.1 bzrtools_upstream
> bzr branch lp:ubuntu/bzrtools -r 50 bzrtools_ubuntu
> cd bzrtools_ubuntu
> bzr merge ../bzrtools_upstream
>
> (There are other conflicts, but *debian/changelog* is not one of them)

Great, thanks for your work on this.

29 + merge.Merger.hooks.install_named_hook(
30 + 'merge_file_content', changelog_merge_hook_factory,
31 + 'Changelog file merge')

We should put "Debian" in there somewhere. I can do that on merge.

Other than that this looks fine to me. I want to discuss the conflict
issue before merging though.

Thanks,

James

review: Needs Information
Revision history for this message
James Westby (james-w) wrote :

> This adds a per-file commit hook, which checks
> a) A Branch config called
> changelog_hook_enabled = True
> b) A file named 'changelog' or 'debian/changelog'
>
> If both hold true, and it perceives a conflict in the changelog file, it
> essentially runs Scott's merge_changelog code. This will always result in a
> conflict-free changelog file. The current algorithm is "prefer-mine". So if
> both changelogs have a section with a matching 'version', the text present in
> "this" are used.

That sounds wrong to me, why wouldn't it conflict those blocks?

> There are some new tests for this, but I've also manually tested merging
> lp:ubuntu/bzrtools @-r4.3.1 into -r 50. And it seemed to do a nice clean merge
> (without the plugin enabled it conflicted as expected.) As an example:
>
> bzr branch lp:ubuntu/bzrtools -r 4.3.1 bzrtools_upstream
> bzr branch lp:ubuntu/bzrtools -r 50 bzrtools_ubuntu
> cd bzrtools_ubuntu
> bzr merge ../bzrtools_upstream
>
> (There are other conflicts, but *debian/changelog* is not one of them)

Great, thanks for your work on this.

29 + merge.Merger.hooks.install_named_hook(
30 + 'merge_file_content', changelog_merge_hook_factory,
31 + 'Changelog file merge')

We should put "Debian" in there somewhere. I can do that on merge.

Other than that this looks fine to me. I want to discuss the conflict
issue before merging though.

Thanks,

James

review: Needs Information
Revision history for this message
James Westby (james-w) wrote :

> This adds a per-file commit hook, which checks
> a) A Branch config called
> changelog_hook_enabled = True
> b) A file named 'changelog' or 'debian/changelog'
>
> If both hold true, and it perceives a conflict in the changelog file, it
> essentially runs Scott's merge_changelog code. This will always result in a
> conflict-free changelog file. The current algorithm is "prefer-mine". So if
> both changelogs have a section with a matching 'version', the text present in
> "this" are used.

That sounds wrong to me, why wouldn't it conflict those blocks?

> There are some new tests for this, but I've also manually tested merging
> lp:ubuntu/bzrtools @-r4.3.1 into -r 50. And it seemed to do a nice clean merge
> (without the plugin enabled it conflicted as expected.) As an example:
>
> bzr branch lp:ubuntu/bzrtools -r 4.3.1 bzrtools_upstream
> bzr branch lp:ubuntu/bzrtools -r 50 bzrtools_ubuntu
> cd bzrtools_ubuntu
> bzr merge ../bzrtools_upstream
>
> (There are other conflicts, but *debian/changelog* is not one of them)

Great, thanks for your work on this.

29 + merge.Merger.hooks.install_named_hook(
30 + 'merge_file_content', changelog_merge_hook_factory,
31 + 'Changelog file merge')

We should put "Debian" in there somewhere. I can do that on merge.

Other than that this looks fine to me. I want to discuss the conflict
issue before merging though.

Thanks,

James

review: Needs Information
Revision history for this message
John A Meinel (jameinel) wrote :
Download full text (4.0 KiB)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

James Westby wrote:
> Review: Needs Information
>> This adds a per-file commit hook, which checks
>> a) A Branch config called
>> changelog_hook_enabled = True
>> b) A file named 'changelog' or 'debian/changelog'
>>
>> If both hold true, and it perceives a conflict in the changelog file, it
>> essentially runs Scott's merge_changelog code. This will always result in a
>> conflict-free changelog file. The current algorithm is "prefer-mine". So if
>> both changelogs have a section with a matching 'version', the text present in
>> "this" are used.
>
> That sounds wrong to me, why wouldn't it conflict those blocks?

So the main thing is that I took the code that Scott had provided (and
which I guess people had been using with decent success) and turned it
into a per-file merge hook.

I think in the long run, we can improve the code to change it from a
2-way merger into a 3-way merger. (So if both sides update things, we
can tell when one supersedes the base rather than having to conflict
just because there was a change on one side.)

However, I felt this would still be an improvement over the status quo,
and we could deal with bugs/improving things going forward.

>
>> There are some new tests for this, but I've also manually tested merging
>> lp:ubuntu/bzrtools @-r4.3.1 into -r 50. And it seemed to do a nice clean merge
>> (without the plugin enabled it conflicted as expected.) As an example:
>>
>> bzr branch lp:ubuntu/bzrtools -r 4.3.1 bzrtools_upstream
>> bzr branch lp:ubuntu/bzrtools -r 50 bzrtools_ubuntu
>> cd bzrtools_ubuntu
>> bzr merge ../bzrtools_upstream
>>
>> (There are other conflicts, but *debian/changelog* is not one of them)
>
> Great, thanks for your work on this.
>
> 29 + merge.Merger.hooks.install_named_hook(
> 30 + 'merge_file_content', changelog_merge_hook_factory,
> 31 + 'Changelog file merge')
>
> We should put "Debian" in there somewhere. I can do that on merge.
>

'merge_file_content' is the name of the hook,
'changelog_merge_hook_factory' is just the function name, but you could
call it "Debian changelog file merge" if you wanted. I'm not sure if it
really matters.

> Other than that this looks fine to me. I want to discuss the conflict
> issue before merging though.
>
> Thanks,
>
> James
>

I think there is a lot that could be done. I'm not 100% sure about
Scott's parser, but it is independent of the per-file merge hook. Also,
I believe the other parts of the code depend on 'python-debian' for
doing stuff like Version, so we should probably grab that rather than
using his parser.

I think Jelmer said that python-debian has a lot more work in it to
handle really-old changelog files that pre-date the standardized
formatting. (Though really-old formats are most likely not going to be
part of the interesting merge sections.)

To elaborate a bit about the issues of a 2-way merger. If you have 2
changelog entries (1.0-1 and 1.1-1.) If one side introduces 1.2-1 and
updates 1.1-1 to fix a typo. And the other side introduces
1.1-1~ubuntu1. A 2-way merger can only tell that 1.1-1 is different on
both sides, and has to generate a conflict. A 3-way merger...

Read more...

Revision history for this message
James Westby (james-w) wrote :

> 'merge_file_content' is the name of the hook,
> 'changelog_merge_hook_factory' is just the function name, but you could
> call it "Debian changelog file merge" if you wanted. I'm not sure if it
> really matters.

Yeah, I meant the description, so that people don't ask about why
it isn't merging their non-Debian changelog.

> I think there is a lot that could be done. I'm not 100% sure about
> Scott's parser, but it is independent of the per-file merge hook. Also,
> I believe the other parts of the code depend on 'python-debian' for
> doing stuff like Version, so we should probably grab that rather than
> using his parser.

Version would be good to use.

> I think Jelmer said that python-debian has a lot more work in it to
> handle really-old changelog files that pre-date the standardized
> formatting. (Though really-old formats are most likely not going to be
> part of the interesting merge sections.)

I think that this approach may be better actually, parsing as little as
possible leaves less room for problems.

> To elaborate a bit about the issues of a 2-way merger. If you have 2
> changelog entries (1.0-1 and 1.1-1.) If one side introduces 1.2-1 and
> updates 1.1-1 to fix a typo. And the other side introduces
> 1.1-1~ubuntu1. A 2-way merger can only tell that 1.1-1 is different on
> both sides, and has to generate a conflict. A 3-way merger could tell
> that the 1.1-1 version in one side was the same as the version in the
> BASE, and thus can take just the newer 1.1-1.
>
> I think Scott chose "prefer-mine" because it was an easy answer, people
> don't often update old entries, and he didn't want to have to worry (or
> didn't think about) conflicts. It seemed that people had been using his
> code successfully for a while, so I figured it would be a reasonable
> starting point for this.
>
> I'd be willing to open some bugs on ways that the per-file merge hook
> could be improved, which we can try to get to as they seem to actually
> be needed.

I think that's a good compromise, please do this.

Thanks,

James

review: Approve
Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

...
>>
>> I'd be willing to open some bugs on ways that the per-file merge hook
>> could be improved, which we can try to get to as they seem to actually
>> be needed.
>
> I think that's a good compromise, please do this.
>
> Thanks,
>
> James
>

https://bugs.edge.launchpad.net/bzr-builddeb/+bug/516056
(should use python-debian for parsing)

https://bugs.edge.launchpad.net/bzr-builddeb/+bug/516060
(should use a 3-way merge algorithm)

John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAktoOR4ACgkQJdeBCYSNAAOvEgCg0Zo7mReZwTLTVJZOENy5JTWH
3bkAnigYN7crOHClKSej+67nIq+Bvv/n
=E8Yw
-----END PGP SIGNATURE-----

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '__init__.py'
2--- __init__.py 2010-01-28 11:11:25 +0000
3+++ __init__.py 2010-01-29 14:09:14 +0000
4@@ -25,7 +25,11 @@
5
6 import os
7
8-from bzrlib import msgeditor
9+import bzrlib
10+from bzrlib import (
11+ merge,
12+ msgeditor,
13+ )
14 from bzrlib.commands import plugin_cmds
15 from bzrlib.directory_service import directories
16
17@@ -110,9 +114,23 @@
18 "the commit message")
19
20
21+if getattr(merge, 'ConfigurableFileMerger', None) is None:
22+ raise ImportError(
23+ 'need at least bzr 2.1.0rc2 (you use %r)', bzrlib.version_info)
24+else:
25+ def changelog_merge_hook_factory(merger):
26+ from bzrlib.plugins.builddeb import merge_changelog
27+ return merge_changelog.ChangeLogFileMerge(merger)
28+
29+ merge.Merger.hooks.install_named_hook(
30+ 'merge_file_content', changelog_merge_hook_factory,
31+ 'Changelog file merge')
32+
33+
34 try:
35 from bzrlib.revisionspec import revspec_registry
36- revspec_registry.register_lazy("package:", "bzrlib.plugins.builddeb.revspec", "RevisionSpec_package")
37+ revspec_registry.register_lazy("package:",
38+ "bzrlib.plugins.builddeb.revspec", "RevisionSpec_package")
39 except ImportError:
40 from bzrlib.revisionspec import SPEC_TYPES
41 from bzrlib.plugins.builddeb.revspec import RevisionSpec_package
42
43=== added file 'merge_changelog.py'
44--- merge_changelog.py 1970-01-01 00:00:00 +0000
45+++ merge_changelog.py 2010-01-29 14:09:14 +0000
46@@ -0,0 +1,262 @@
47+#!/usr/bin/env python
48+# -*- coding: utf-8 -*-
49+# Copyright ? 2008 Canonical Ltd.
50+# Author: Scott James Remnant <scott@ubuntu.com>.
51+# Hacked up by: Bryce Harrington <bryce@ubuntu.com>
52+#
53+# This program is free software: you can redistribute it and/or modify
54+# it under the terms of version 3 of the GNU General Public License as
55+# published by the Free Software Foundation.
56+#
57+# This program is distributed in the hope that it will be useful,
58+# but WITHOUT ANY WARRANTY; without even the implied warranty of
59+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
60+# GNU General Public License for more details.
61+#
62+# You should have received a copy of the GNU General Public License
63+# along with this program. If not, see <http://www.gnu.org/licenses/>.
64+
65+import re
66+
67+from bzrlib import (
68+ merge,
69+ )
70+
71+class ChangeLogFileMerge(merge.ConfigurableFileMerger):
72+
73+ name_prefix = 'deb_changelog'
74+ default_files = ['debian/changelog']
75+
76+ def merge_text(self, params):
77+ return 'success', merge_changelog(params.this_lines, params.other_lines)
78+
79+
80+########################################################################
81+# Changelog Management
82+########################################################################
83+
84+# Regular expression for top of debian/changelog
85+CL_RE = re.compile(r'^(\w[-+0-9a-z.]*) \(([^\(\) \t]+)\)((\s+[-0-9a-z]+)+)\;',
86+ re.IGNORECASE)
87+
88+def merge_changelog(left_changelog_lines, right_changelog_lines):
89+ """Merge a changelog file."""
90+
91+ left_cl = read_changelog(left_changelog_lines)
92+ right_cl = read_changelog(right_changelog_lines)
93+
94+ content = []
95+ # TODO: This is not a 3-way merge, but a 2-way merge
96+ # The resolution is currently 'if left and right have texts that have
97+ # the same "version" string, use left', aka "prefer-mine".
98+ # We could introduce BASE, and cause conflicts, or appropriately
99+ # resolve, etc.
100+ # Note also that this code is only invoked when there is a
101+ # left-and-right change, so merging a pure-right change will take all
102+ # changes.
103+ for right_ver, right_text in right_cl:
104+ while len(left_cl) and left_cl[0][0] > right_ver:
105+ (left_ver, left_text) = left_cl.pop(0)
106+ content.append(left_text)
107+ content.append('\n')
108+
109+ while len(left_cl) and left_cl[0][0] == right_ver:
110+ (left_ver, left_text) = left_cl.pop(0)
111+
112+ content.append(right_text)
113+ content.append('\n')
114+
115+ for left_ver, left_text in left_cl:
116+ content.append(left_text)
117+ content.append('\n')
118+
119+ return content
120+
121+
122+def read_changelog(lines):
123+ """Return a parsed changelog file."""
124+ entries = []
125+
126+ (ver, text) = (None, "")
127+ for line in lines:
128+ match = CL_RE.search(line)
129+ if match:
130+ try:
131+ ver = Version(match.group(2))
132+ except ValueError:
133+ ver = None
134+
135+ text += line
136+ elif line.startswith(" -- "):
137+ if ver is None:
138+ ver = Version("0")
139+
140+ text += line
141+ entries.append((ver, text))
142+ (ver, text) = (None, "")
143+ elif len(line.strip()) or ver is not None:
144+ text += line
145+
146+ if len(text):
147+ entries.append((ver, text))
148+
149+ return entries
150+
151+########################################################################
152+# Version parsing code
153+########################################################################
154+# Regular expressions make validating things easy
155+valid_epoch = re.compile(r'^[0-9]+$')
156+valid_upstream = re.compile(r'^[A-Za-z0-9+:.~-]*$')
157+valid_revision = re.compile(r'^[A-Za-z0-9+.~]+$')
158+
159+# Character comparison table for upstream and revision components
160+cmp_table = "~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-.:"
161+
162+
163+class Version(object):
164+ """Debian version number.
165+
166+ This class is designed to be reasonably transparent and allow you
167+ to write code like:
168+
169+ | s.version >= '1.100-1'
170+
171+ The comparison will be done according to Debian rules, so '1.2' will
172+ compare lower.
173+
174+ Properties:
175+ epoch Epoch
176+ upstream Upstream version
177+ revision Debian/local revision
178+ """
179+
180+ def __init__(self, ver):
181+ """Parse a string or number into the three components."""
182+ self.epoch = 0
183+ self.upstream = None
184+ self.revision = None
185+
186+ ver = str(ver)
187+ if not len(ver):
188+ raise ValueError
189+
190+ # Epoch is component before first colon
191+ idx = ver.find(":")
192+ if idx != -1:
193+ self.epoch = ver[:idx]
194+ if not len(self.epoch):
195+ raise ValueError
196+ if not valid_epoch.search(self.epoch):
197+ raise ValueError
198+ ver = ver[idx+1:]
199+
200+ # Revision is component after last hyphen
201+ idx = ver.rfind("-")
202+ if idx != -1:
203+ self.revision = ver[idx+1:]
204+ if not len(self.revision):
205+ raise ValueError
206+ if not valid_revision.search(self.revision):
207+ raise ValueError
208+ ver = ver[:idx]
209+
210+ # Remaining component is upstream
211+ self.upstream = ver
212+ if not len(self.upstream):
213+ raise ValueError
214+ if not valid_upstream.search(self.upstream):
215+ raise ValueError
216+
217+ self.epoch = int(self.epoch)
218+
219+ def getWithoutEpoch(self):
220+ """Return the version without the epoch."""
221+ str = self.upstream
222+ if self.revision is not None:
223+ str += "-%s" % (self.revision,)
224+ return str
225+
226+ without_epoch = property(getWithoutEpoch)
227+
228+ def __str__(self):
229+ """Return the class as a string for printing."""
230+ str = ""
231+ if self.epoch > 0:
232+ str += "%d:" % (self.epoch,)
233+ str += self.upstream
234+ if self.revision is not None:
235+ str += "-%s" % (self.revision,)
236+ return str
237+
238+ def __repr__(self):
239+ """Return a debugging representation of the object."""
240+ return "<%s epoch: %d, upstream: %r, revision: %r>" \
241+ % (self.__class__.__name__, self.epoch,
242+ self.upstream, self.revision)
243+
244+ def __cmp__(self, other):
245+ """Compare two Version classes."""
246+ other = Version(other)
247+
248+ result = cmp(self.epoch, other.epoch)
249+ if result != 0: return result
250+
251+ result = deb_cmp(self.upstream, other.upstream)
252+ if result != 0: return result
253+
254+ result = deb_cmp(self.revision or "", other.revision or "")
255+ if result != 0: return result
256+
257+ return 0
258+
259+
260+def strcut(str, idx, accept):
261+ """Cut characters from str that are entirely in accept."""
262+ ret = ""
263+ while idx < len(str) and str[idx] in accept:
264+ ret += str[idx]
265+ idx += 1
266+
267+ return (ret, idx)
268+
269+def deb_order(str, idx):
270+ """Return the comparison order of two characters."""
271+ if idx >= len(str):
272+ return 0
273+ elif str[idx] == "~":
274+ return -1
275+ else:
276+ return cmp_table.index(str[idx])
277+
278+def deb_cmp_str(x, y):
279+ """Compare two strings in a deb version."""
280+ idx = 0
281+ while (idx < len(x)) or (idx < len(y)):
282+ result = deb_order(x, idx) - deb_order(y, idx)
283+ if result < 0:
284+ return -1
285+ elif result > 0:
286+ return 1
287+
288+ idx += 1
289+
290+ return 0
291+
292+def deb_cmp(x, y):
293+ """Implement the string comparison outlined by Debian policy."""
294+ x_idx = y_idx = 0
295+ while x_idx < len(x) or y_idx < len(y):
296+ # Compare strings
297+ (x_str, x_idx) = strcut(x, x_idx, cmp_table)
298+ (y_str, y_idx) = strcut(y, y_idx, cmp_table)
299+ result = deb_cmp_str(x_str, y_str)
300+ if result != 0: return result
301+
302+ # Compare numbers
303+ (x_str, x_idx) = strcut(x, x_idx, "0123456789")
304+ (y_str, y_idx) = strcut(y, y_idx, "0123456789")
305+ result = cmp(int(x_str or "0"), int(y_str or "0"))
306+ if result != 0: return result
307+
308+ return 0
309
310=== modified file 'tests/__init__.py'
311--- tests/__init__.py 2010-01-29 09:30:27 +0000
312+++ tests/__init__.py 2010-01-29 14:09:14 +0000
313@@ -116,6 +116,7 @@
314 'test_config',
315 'test_hooks',
316 'test_import_dsc',
317+ 'test_merge_changelog',
318 'test_merge_package',
319 'test_merge_upstream',
320 'test_repack_tarball_extra',
321
322=== added file 'tests/test_merge_changelog.py'
323--- tests/test_merge_changelog.py 1970-01-01 00:00:00 +0000
324+++ tests/test_merge_changelog.py 2010-01-29 14:09:14 +0000
325@@ -0,0 +1,128 @@
326+# Copyright (C) 2010 Canonical Ltd
327+#
328+# This file is part of bzr-builddeb.
329+#
330+# bzr-builddeb is free software; you can redistribute it and/or modify
331+# it under the terms of the GNU General Public License as published by
332+# the Free Software Foundation; either version 2 of the License, or
333+# (at your option) any later version.
334+#
335+# bzr-builddeb is distributed in the hope that it will be useful,
336+# but WITHOUT ANY WARRANTY; without even the implied warranty of
337+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
338+# GNU General Public License for more details.
339+#
340+# You should have received a copy of the GNU General Public License
341+# along with bzr-builddeb; if not, write to the Free Software
342+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
343+#
344+
345+"""Tests for the merge_changelog code."""
346+
347+from bzrlib import (
348+ memorytree,
349+ merge,
350+ tests,
351+ )
352+from bzrlib.plugins import builddeb
353+from bzrlib.plugins.builddeb import merge_changelog
354+
355+
356+class TestReadChangelog(tests.TestCase):
357+
358+ def test_read_changelog(self):
359+ lines = """\
360+psuedo-prog (1.1.1-2) unstable; urgency=low
361+
362+ * New upstream release.
363+ * Awesome bug fixes.
364+
365+ -- Joe Foo <joe@example.com> Thu, 28 Jan 2010 10:45:44 +0000
366+""".splitlines(True)
367+
368+
369+ entries = merge_changelog.read_changelog(lines)
370+ self.assertEqual(1, len(entries))
371+
372+
373+class TestMergeChangelog(tests.TestCase):
374+
375+ def assertMergeChangelog(self, expected_lines, this_lines, other_lines):
376+ merged_lines = merge_changelog.merge_changelog(this_lines, other_lines)
377+ self.assertEqualDiff(''.join(expected_lines), ''.join(merged_lines))
378+
379+ def test_merge_by_version(self):
380+ v_111_2 = """\
381+psuedo-prog (1.1.1-2) unstable; urgency=low
382+
383+ * New upstream release.
384+ * Awesome bug fixes.
385+
386+ -- Joe Foo <joe@example.com> Thu, 28 Jan 2010 10:45:44 +0000
387+
388+""".splitlines(True)
389+
390+ v_112_1 = """\
391+psuedo-prog (1.1.2-1) unstable; urgency=low
392+
393+ * New upstream release.
394+ * No bug fixes :(
395+
396+ -- Barry Foo <barry@example.com> Thu, 27 Jan 2010 10:45:44 +0000
397+
398+""".splitlines(True)
399+
400+ v_001_1 = """\
401+psuedo-prog (0.0.1-1) unstable; urgency=low
402+
403+ * New project released!!!!
404+ * No bugs evar
405+
406+ -- Barry Foo <barry@example.com> Thu, 27 Jan 2010 10:00:44 +0000
407+
408+""".splitlines(True)
409+
410+ this_lines = v_111_2 + v_001_1
411+ other_lines = v_112_1 + v_001_1
412+ expected_lines = v_112_1 + v_111_2 + v_001_1
413+ self.assertMergeChangelog(expected_lines, this_lines, other_lines)
414+ self.assertMergeChangelog(expected_lines, other_lines, this_lines)
415+
416+
417+class TestChangelogHook(tests.TestCaseWithMemoryTransport):
418+
419+ def make_params(self):
420+ builder = self.make_branch_builder('source')
421+ builder.start_series()
422+ builder.build_snapshot('A', None, [
423+ ('add', ('', 'TREE_ROOT', 'directory', None)),
424+ ('add', ('debian', 'deb-id', 'directory', None)),
425+ ('add', ('debian/changelog', 'c-id', 'file', '')),
426+ ('add', ('changelog', 'o-id', 'file', '')),
427+ ])
428+ builder.finish_series()
429+ the_branch = builder.get_branch()
430+
431+ tree = memorytree.MemoryTree.create_on_branch(the_branch)
432+ tree.lock_write()
433+ self.addCleanup(tree.unlock)
434+
435+ class FakeMerger(object):
436+ def __init__(self, this_tree):
437+ self.this_tree = this_tree
438+ def get_lines(self, tree, file_id):
439+ return tree.get_file_lines(file_id)
440+
441+ merger = FakeMerger(tree)
442+ params = merge.MergeHookParams(merger, 'c-id', None, 'file', 'file',
443+ 'this')
444+ return params, merger
445+
446+
447+ def test_changelog_merge_hook_successful(self):
448+ params, merger = self.make_params()
449+ params.other_lines = ['']
450+ file_merger = builddeb.changelog_merge_hook_factory(merger)
451+ result, new_content = file_merger.merge_text(params)
452+ self.assertEqual('success', result)
453+ # We ignore the new_content, as we test that elsewhere

Subscribers

People subscribed via source and target branches