[Pkg-bazaar-commits] r125 ./bzr-builddeb/people/jdw/merge_upstream: Start using tags to mark the upstream imports.

James Westby jw+debian at jameswestby.net
Sun Jun 24 15:01:46 UTC 2007


------------------------------------------------------------
revno: 125
committer: James Westby <jw+debian at jameswestby.net>
branch nick: merge_upstream
timestamp: Sun 2007-06-24 16:01:46 +0100
message:
  Start using tags to mark the upstream imports.
  
  Asking the user for the revision of the last upstream import isn't pretty,
  so start marking them with tags. This isn't perfect, as it requires
  --dirstate-tags, but that has been available for a while.
  
  The UI needs to be tidied up, with more information when things go wrong,
  and a way to override it if there was no import of the last upstream or
  similar.
modified:
  __init__.py
  merge_upstream.py
  specs/new-upstream-release-handling
  tests/test_merge_upstream.py
-------------- next part --------------
=== modified file '__init__.py'
--- a/__init__.py	2007-06-24 10:09:21 +0000
+++ b/__init__.py	2007-06-24 15:01:46 +0000
@@ -291,25 +291,40 @@
 register_command(cmd_builddeb)
 
 class cmd_merge_upstream(Command):
-  """ merges a new upstream version into the current branch
-
-  you need to specify the revision of the last upstream import at the
-  moment
-
+  """Merges a new upstream version into the current branch.
+
+  Takes a new upstream version and merges it in to your branch, so that your
+  packaging changes are applied to the new version.
+
+  You must supply the source to import from, and the version number of the
+  new release. The source can be a .tar.gz, .tar, .tar.bz2, .tgz or .zip
+  archive, or a directory.
   """
-  takes_args = ['filename']
-  takes_options = ['revision']
+  takes_args = ['filename', 'version']
   aliases = ['mu']
 
-  def run(self, filename=None, revision=None):
+  def run(self, filename, version):
 
     from merge_upstream import merge_upstream
-
-    if not revision:
-      raise BzrCommandError("Must specify a revision")
+    from bzrlib.errors import (NoSuchTag,
+                               TagAlreadyExists,
+                               )
 
     tree, relpath = WorkingTree.open_containing('.')
-    merge_upstream(tree, filename, revision)
+    try:
+      merge_upstream(tree, filename, version)
+    # TODO: tidy all of this up, and be more precise in what is wrong and
+    #       what can be done.
+    except NoSuchTag:
+      raise BzrCommandError("The tag of the last upstream import can not be "
+                            "found. You should tag the revision that matches "
+                            "the last upstream version")
+    except TagAlreadyExists:
+      raise BzrCommandError("It appears as though this merge has already "
+                            "been performed, as there is already a tag "
+                            "for this upstream version. If that is not the "
+                            "case then delete that tag and try again.")
+    # TODO: also repack the tarball.
 
 
 register_command(cmd_merge_upstream)

=== modified file 'merge_upstream.py'
--- a/merge_upstream.py	2007-06-23 10:31:23 +0000
+++ b/merge_upstream.py	2007-06-24 15:01:46 +0000
@@ -24,49 +24,91 @@
 import os
 from StringIO import StringIO
 
+from debian_bundle.changelog import Changelog
+
 from bzrlib.errors import (BzrCommandError,
                            NoSuchFile,
+                           NoSuchTag,
+                           TagAlreadyExists,
                            )
 from bzrlib.plugins.bzrtools.upstream_import import (import_tar,
                                                      import_dir,
                                                      import_zip,
                                                      )
 
-# TODO: drop requirement for revision of last upstream, use tags or something
-#       instead.
+from errors import AddChangelogError
+
+# TODO: way of working out new version number.
 # TODO: support using an explicit standalone upstream branch.
 
-def merge_upstream(tree, source, old_revision):
+def make_upstream_tag(version):
+  """Make the name of the tag corresponding to the given version."""
+  return "upstream-%s" % str(version)
+
+
+def lookup_tag(tree):
+   """Look up the last upstream tag in the branch.
+
+   The upstream version of the last entry in debian/changelog in the tree
+   is used in the name of the tag, format 'upstream-<version>'. This tag is
+   then looked up in the branch. The return will be the revision_id that the
+   tag corresponds to.
+
+   :param tree: The tree to read debian/changelog from, and to have it's
+                branch used for the tag lookup.
+   :type tree: WorkingTree
+   :returns: The revision_id corresponding to the tag.
+   :rtype: string
+   :throws: NoSuchTag if the tag is not present.
+   """
+   cl_id = tree.path2id('debian/changelog')
+   if cl_id is None:
+     raise AddChangelogError('debian/changelog')
+   cl = Changelog(tree.get_file_text(cl_id))
+   upstream_version = cl.upstream_version
+   tag = make_upstream_tag(upstream_version)
+   return tree.branch.tags.lookup_tag(tag)
+
+
+def merge_upstream(tree, source, version_number):
     """Merge a new upstream release.
 
     A new upstream release will be extracted and imported in to the branch,
     and then the packaging specific changes merged in to this.
 
-    The revision of the last commit on the upstream "branch", upon which the
-    new upstream will be created. The merge will then be done in to this,
-    and the tree will be left with pending merges, and possibly any conflicts
-    to fix up.
+    The debian/changelog will be opened in the tree for the last upstream
+    version. Then a tag named 'upstream-<version>' will be looked up in the
+    branch and that revision will be used as the last import of upstream. The
+    new version will then be imported on top of this and a new tag will be
+    created using the specified version number. The merge will then be done
+    in to this, and the tree will be left with pending merges, and possibly
+    any conflicts to fix up.
 
     The tree must have no uncommited changes.
 
-    If the specified old_revision is the tip of the tree's branch then
-    a fastforward is done, and will be committed.
+    If the found tag is the tip of the tree's branch then a fastforward is
+    done, and will be committed.
 
     :param tree: The tree upon which to operate.
     :type tree: WorkingTree
     :param source: The filename tarball to import from.
     :type source: string
-    :param old_revision: The revision of the last commit on the upstream
-                         branch.
-    :type old_revision: RevisionSpec
+    :param version_number: The version number of the new upstream.
+    :type version_number: string
     :return: None
+    :throws NoSuchTag: if the tag for the last upstream version is not found.
     """
     if tree.changes_from(tree.basis_tree()).has_changed():
       raise BzrCommandError("Working tree has uncommitted changes.")
     if not os.path.exists(source):
       raise NoSuchFile(source)
+    try:
+      tree.branch.tags.lookup_tag(make_upstream_tag(version_number))
+      raise TagAlreadyExists(make_upstream_tag(version_number))
+    except NoSuchTag:
+      pass
     current_revision = tree.last_revision()
-    revno, rev_id = old_revision.in_branch(tree.branch)
+    rev_id = lookup_tag(tree)
     if rev_id != tree.branch.last_revision():
       tree.revert([], tree.branch.repository.revision_tree(rev_id))
       if os.path.isdir(source):
@@ -88,8 +130,11 @@
         elif source.endswith('.zip'):
             import_zip(tree, open(source, 'rb'))
       tree.set_parent_ids([rev_id])
-      tree.branch.set_last_revision_info(revno, rev_id)
+      tree.branch.set_last_revision_info(
+                     tree.branch.revision_id_to_revno(rev_id), rev_id)
       tree.commit('import upstream from %s' % os.path.basename(source))
+      tree.branch.tags.set_tag(make_upstream_tag(version_number),
+                               tree.branch.last_revision())
       tree.merge_from_branch(tree.branch, to_revision=current_revision)
     else:
       # Fast forward the merge.

=== modified file 'specs/new-upstream-release-handling'
--- a/specs/new-upstream-release-handling	2007-01-31 22:33:55 +0000
+++ b/specs/new-upstream-release-handling	2007-06-24 15:01:46 +0000
@@ -67,6 +67,49 @@
 and does the right thing, or errors out if it doesn't have the information to
 decide. Perhaps it could prompt for the information it needs.
 
+Normal mode
+-----------
+
+In normal mode, where the branch stores the whole package and new releases
+are imported from tarballs and upstream branch is used. In this branch there
+is a commit for each new upstream version. The packaging changes are then done
+on a separate branch.
+
+The upstream branch does not have to be explicit, it can be handled by the
+plugin in the simple branch.
+
+If the upstream branch is explicit, then the plugin opens the branch, imports
+the tarball, and then merges the changes back in to the packaging branch,
+where the user can make the necessary changes, and then commit.
+
+If the upstream branch is implicit, then the last import needs to be
+identified, and the tree set to that revision, and the import done on top of
+that, and then the old head merged in to the newly created one. This is a
+merge in the other direction to the explicit branch case, but makes more sense
+with the implicit branch handling strategy (the 'loom' idea).
+
+The last import commit must be known, there are several ways this could be
+done:
+
+ * Have the user specify it.
+ * Store it in a text file.
+ * Use a revision property.
+ * Use tags.
+
+The last seems the most appropriate, and tags have been available in Bazaar
+for a while now. At each import the commit can be tagged with
+'upstream-<version>'. When the next merge is performed the old version number
+can be derived from the changelog, and the tag looked up. If it is not present
+then the user can be requested to tag the version themseleves. There is a
+chance that the branch doesn't include the old version, in which case the
+user could provide an older version to look up, or if there is no upstream
+branch a new one could be created.
+
+The tags could also be seen as useful themselves, as it makes it easy to see
+things like the current packaging changes by diffing te HEAD with the tag
+for the last upstream version. If tags are also made for packaging releases
+then they could be even more useful.
+
 Code changes
 ------------
 

=== modified file 'tests/test_merge_upstream.py'
--- a/tests/test_merge_upstream.py	2007-06-23 10:31:23 +0000
+++ b/tests/test_merge_upstream.py	2007-06-24 15:01:46 +0000
@@ -21,9 +21,13 @@
 import os
 import shutil
 
+from debian_bundle.changelog import Changelog, Version
+
 from bzrlib.errors import (BzrCommandError,
                            InvalidRevisionSpec,
                            NoSuchFile,
+                           NoSuchTag,
+                           TagAlreadyExists,
                            )
 from bzrlib.revisionspec import RevisionSpec
 from bzrlib.tests import TestCaseWithTransport
@@ -40,9 +44,6 @@
   finally:
     f.close()
 
-def make_revspec(rev_id):
-  """Turn the rev_id passed in to a revision spec."""
-  return RevisionSpec.from_string('revid:' + rev_id)
 
 class TestMergeUpstreamNormal(TestCaseWithTransport):
   """Test that builddeb can merge upstream in normal mode"""
@@ -56,13 +57,29 @@
     self.build_tarball()
 
   def make_first_upstream_commit(self):
-    self.wt = self.make_branch_and_tree('.')
+    self.wt = self.make_branch_and_tree('.', format='dirstate-tags')
     self.build_tree(['README', 'CHANGELOG'])
     self.wt.add(['README', 'CHANGELOG'], ['README-id', 'CHANGELOG-id'])
     self.wt.commit('upstream version 1', rev_id=self.upstream_rev_id_1)
 
   def make_first_debian_commit(self):
-    self.build_tree(['debian/', 'debian/changelog'])
+    self.build_tree(['debian/'])
+    cl = Changelog()
+    cl.new_block(package='package',
+                 version=Version('0.1-1'),
+                 distributions='unstable',
+                 urgency='low',
+                 author='James Westby <jw+debian at jameswestby.net',
+                 date='Thu,  3 Aug 2006 19:16:22 +0100',
+                 )
+    cl.add_change('');
+    cl.add_change('  * Initial packaging.');
+    cl.add_change('');
+    f = open('debian/changelog', 'wb')
+    try:
+      cl.write_to_open_file(f)
+    finally:
+      f.close()
     self.wt.add(['debian/', 'debian/changelog'],
                 ['debian-id', 'debian-changelog-id'])
     self.wt.commit('debian version 1-1', rev_id='debian-1-1')
@@ -81,11 +98,10 @@
     :rtype: WorkingTree
     """
     self.make_first_upstream_commit()
-    old_upstream_revision = self.wt.branch.last_revision()
+    self.wt.branch.tags.set_tag('upstream-0.1', self.wt.branch.last_revision())
     self.make_first_debian_commit()
     self.make_new_upstream()
-    merge_upstream(self.wt, self.upstream_tarball,
-                   make_revspec(old_upstream_revision))
+    merge_upstream(self.wt, self.upstream_tarball, '0.2')
     return self.wt
 
   def test_merge_upstream(self):
@@ -93,24 +109,23 @@
     self.check_simple_merge_results()
 
   def test_merge_upstream_requires_clean_tree(self):
-    wt = self.make_branch_and_tree('.')
+    wt = self.make_branch_and_tree('.', format='dirstate-tags')
     self.build_tree(['file'])
     wt.add(['file'])
     self.assertRaises(BzrCommandError, merge_upstream, wt, 'source', 1)
 
   def test_merge_upstream_handles_no_source(self):
     self.make_first_upstream_commit()
-    old_upstream_revision = self.wt.branch.last_revision()
+    self.wt.branch.tags.set_tag('upstream-0.1', self.wt.branch.last_revision())
     self.make_first_debian_commit()
-    self.assertRaises(NoSuchFile, merge_upstream, self.wt, 'source',
-                      make_revspec(old_upstream_revision))
+    self.assertRaises(NoSuchFile, merge_upstream, self.wt, 'source', '0.2')
 
   def test_merge_upstream_handles_invalid_revision(self):
     self.make_first_upstream_commit()
     self.make_first_debian_commit()
     self.make_new_upstream()
-    self.assertRaises(InvalidRevisionSpec, merge_upstream, self.wt,
-                      self.upstream_tarball, make_revspec('NOTAREVID'))
+    self.assertRaises(NoSuchTag, merge_upstream, self.wt,
+                      self.upstream_tarball, '0.2')
 
   def test_merge_upstream_handles_last_revision(self):
     """Test handling of merging in to tip.
@@ -118,10 +133,31 @@
     If the upstream revision is said to be at the latest revision in the
     branch the code should do a fast-forward.
     """
-    self.make_first_upstream_commit()
+    self.wt = self.make_branch_and_tree('.', format='dirstate-tags')
+    self.build_tree(['README', 'CHANGELOG', 'debian/'])
+    cl = Changelog()
+    cl.new_block(package='package',
+                 version=Version('0.1-1'),
+                 distributions='unstable',
+                 urgency='low',
+                 author='James Westby <jw+debian at jameswestby.net',
+                 date='Thu,  3 Aug 2006 19:16:22 +0100',
+                 )
+    cl.add_change('');
+    cl.add_change('  * Initial packaging.');
+    cl.add_change('');
+    f = open('debian/changelog', 'wb')
+    try:
+      cl.write_to_open_file(f)
+    finally:
+      f.close()
+    self.wt.add(['README', 'CHANGELOG', 'debian', 'debian/changelog'],
+                ['README-id', 'CHANGELOG-id', 'debian-id',
+                 'debian-changelog-id'])
+    self.wt.commit('upstream version 1', rev_id=self.upstream_rev_id_1)
+    self.wt.branch.tags.set_tag('upstream-0.1', self.wt.branch.last_revision())
     self.make_new_upstream()
-    revspec = make_revspec(self.wt.branch.last_revision())
-    merge_upstream(self.wt, self.upstream_tarball, revspec)
+    merge_upstream(self.wt, self.upstream_tarball, '0.2')
     wt = self.wt
     delta = wt.changes_from(wt.basis_tree(), want_unversioned=True,
                             want_unchanged=True)
@@ -141,13 +177,22 @@
                      'import upstream from %s' % \
                      os.path.basename(self.upstream_tarball))
 
+  def test_merge_upstream_new_tag_extant(self):
+    self.make_first_upstream_commit()
+    self.wt.branch.tags.set_tag('upstream-0.1', self.wt.branch.last_revision())
+    self.make_first_debian_commit()
+    self.make_new_upstream()
+    self.wt.branch.tags.set_tag('upstream-0.2', self.wt.branch.last_revision())
+    self.assertRaises(TagAlreadyExists, merge_upstream, self.wt,
+                      self.upstream_tarball, '0.2')
+
   def perform_conflicted_merge(self):
     self.make_first_upstream_commit()
-    revspec = make_revspec(self.wt.branch.last_revision())
+    self.wt.branch.tags.set_tag('upstream-0.1', self.wt.branch.last_revision())
     write_to_file('CHANGELOG', 'debian version\n')
     self.make_first_debian_commit()
     self.make_new_upstream_with_debian()
-    merge_upstream(self.wt, self.upstream_tarball, revspec)
+    merge_upstream(self.wt, self.upstream_tarball, '0.2')
     return self.wt
 
   def test_merge_upstream_gives_correct_tree_on_conficts(self):
@@ -221,6 +266,11 @@
                      'import upstream from %s' % \
                      os.path.basename(self.upstream_tarball))
 
+  def test_merge_upstream_tags_new_version(self):
+    wt = self.perform_conflicted_merge()
+    rh = wt.branch.revision_history()
+    self.assertEqual(wt.branch.tags.lookup_tag('upstream-0.2'), rh[1])
+
   def check_simple_merge_results(self):
     wt = self.wt
     basis = wt.basis_tree()
@@ -248,4 +298,5 @@
     self.assertEqual(wt.branch.repository.get_revision(rh[1]).message,
                      'import upstream from %s' % \
                      os.path.basename(self.upstream_tarball))
+    self.assertEqual(wt.branch.tags.lookup_tag('upstream-0.2'), rh[1])
 



More information about the Pkg-bazaar-commits mailing list