Fixes release syncing issue; adds better error checking.

* Ticket matching is more robust
* Don't sync the sync comments
* Better error handling in pair()
M .ditz/issue-8f7f150d0c575b55bd15bbb5bba4b7285ff7e942.yaml +5 -1
@@ 5,7 5,7 @@ desc: |-
   references with ditz ID references when syncing.
 type: :feature
 component: ditz-trac
-release: 
+release: "2010.3"
 reporter: Sean Russell <ser@ser1.net>
 status: :unstarted
 disposition: 

          
@@ 22,4 22,8 @@ log_events:
   - ser
   - commented
   - Ticket synced from ditz by Sean Russell <ser@ser1.net>
+- - 2010-01-31 16:10:58.095399 Z
+  - Sean Russell <ser@ser1.net>
+  - assigned to release 2010.3 from unassigned
+  - ""
 trac_id: 12

          
A => .ditz/issue-93b851097b899d5aa10f15a03144893db3ef7a81.yaml +37 -0
@@ 0,0 1,37 @@ 
+--- !ditz.rubyforge.org,2008-03-06/issue 
+title: Create milestones/releases/components at beginning
+desc: |-
+  Right now, milestones, releases, and components are created when they're
+  encountered in tickets/issues.  Change this so that they're created once,
+  at the beginning -- it'll be faster, and it'll allow them to show up in
+  the environment that is synced.
+type: :feature
+component: ditz-trac
+release: "2010.3"
+reporter: Sean Russell <ser@ser1.net>
+status: :unstarted
+disposition: 
+creation_time: 2010-01-30 22:15:54.689150 Z
+references: []
+
+id: 93b851097b899d5aa10f15a03144893db3ef7a81
+log_events: 
+- - 2010-01-30 22:15:54.689792 Z
+  - Sean Russell <ser@ser1.net>
+  - created
+  - ""
+- - 2010-01-30 22:18:00 Z
+  - ser
+  - commented
+  - Ticket synced from ditz by Sean Russell <ser@ser1.net>
+- - 2010-01-31 16:10:24.567394 Z
+  - Sean Russell <ser@ser1.net>
+  - commented
+  - |-
+    Milestones and trac components are created at beginning.  Only the ditz
+    side needs to be changed.
+- - 2010-01-31 16:10:34.991411 Z
+  - Sean Russell <ser@ser1.net>
+  - assigned to release 2010.3 from unassigned
+  - ""
+trac_id: 14

          
M .ditz/issue-9bfea1cdc188c9047e6d0b0b95eb931133997bd1.yaml +11 -3
@@ 5,10 5,10 @@ desc: |-
   graceful failing in the code.
 type: :bugfix
 component: ditz-trac
-release: 
+release: "2010.2"
 reporter: Sean Russell <ser@ser1.net>
-status: :unstarted
-disposition: 
+status: :closed
+disposition: :fixed
 creation_time: 2010-01-28 02:26:09.882404 Z
 references: []
 

          
@@ 22,4 22,12 @@ log_events:
   - ser
   - commented
   - Ticket synced from ditz by Sean Russell <ser@ser1.net>
+- - 2010-01-31 16:08:05.019367 Z
+  - Sean Russell <ser@ser1.net>
+  - assigned to release 2010.2 from unassigned
+  - ""
+- - 2010-01-31 16:08:12.007292 Z
+  - Sean Russell <ser@ser1.net>
+  - closed with disposition fixed
+  - ""
 trac_id: 6

          
M .ditz/issue-b6c0977924baa85b69eae1737c281ad03ce211af.yaml +5 -1
@@ 3,7 3,7 @@ title: Add support for priority & severi
 desc: ""
 type: :feature
 component: trac to ditz
-release: 
+release: "2010.3"
 reporter: Sean Russell <ser@ser1.net>
 status: :unstarted
 disposition: 

          
@@ 20,4 20,8 @@ log_events:
   - ser
   - commented
   - Ticket synced from ditz by Sean Russell <ser@ser1.net>
+- - 2010-01-31 16:10:49.863399 Z
+  - Sean Russell <ser@ser1.net>
+  - assigned to release 2010.3 from unassigned
+  - ""
 trac_id: 7

          
M .ditz/issue-e9972f258450d6aca85514f2a8d91b3240b66a41.yaml +6 -2
@@ 5,8 5,8 @@ type: :bugfix
 component: ditz-trac
 release: "2010.2"
 reporter: Sean Russell <ser@ser1.net>
-status: :unstarted
-disposition: 
+status: :closed
+disposition: :fixed
 creation_time: 2010-01-27 13:34:08.089011 Z
 references: []
 

          
@@ 28,4 28,8 @@ log_events:
   - ser
   - commented
   - Ticket synced from ditz by Sean Russell <ser@ser1.net>
+- - 2010-01-31 16:07:50.107366 Z
+  - Sean Russell <ser@ser1.net>
+  - closed with disposition fixed
+  - Pair is more robust now, and exceptions are handled better.
 trac_id: 4

          
M .ditz/project.yaml +15 -2
@@ 24,10 24,23 @@ releases:
     - ""
 - !ditz.rubyforge.org,2008-03-06/release 
   name: "2010.2"
-  status: :unreleased
-  release_time: 
+  status: :released
+  release_time: 2010-01-31 16:08:56.595794 Z
   log_events: 
   - - 2010-01-28 01:50:24.274350 Z
     - Sean Russell <ser@ser1.net>
     - created
     - ""
+  - - 2010-01-31 16:08:56.595830 Z
+    - Sean Russell <ser@ser1.net>
+    - released
+    - ""
+- !ditz.rubyforge.org,2008-03-06/release 
+  name: "2010.3"
+  status: :unreleased
+  release_time: 
+  log_events: 
+  - - 2010-01-31 16:09:06.159381 Z
+    - Sean Russell <ser@ser1.net>
+    - created
+    - ""

          
M trac-sync.rb +45 -28
@@ 3,7 3,7 @@ 
 # Provides ditz <-> trac synchronization
 #
 # Author::  Sean Russell <seanerussell@gmail.com>
-# Version:: 2010.1
+# Version:: 2010.2
 #
 # Command added:
 #   ditz sync: synchronize issues with Trac

          
@@ 118,26 118,41 @@ module Ditz
     # ticket.trac_id, and if that fails, by title
     def pair( tickets )
       rv = []
-      issues = @project.issues.clone
+      unpaired_issues = @project.issues.clone
       @logger.debug("Have #{tickets.size} tickets")
       @logger.debug("Tickets: #{tickets.collect{|t|t.summary}.inspect}")
       for ticket in tickets 
-        issue = issues.find { |i| equal?( ticket, i ) }
+        # Try by ID first
+        issue = unpaired_issues.find { |i| ticket.id == i.trac_id }
+        if issue.nil?
+          issue = unpaired_issues.find { |i| ticket.summary == i.title }
+        end
+        # If the ID is the same but the summary isn't then it is probably a rename.
+        # Hopefully.
+
         if issue
+          # However, if we found an issue by title and the IDs don't match,
+          # then somehow the ticket got deleted.  This is bad, but the only thing
+          # that can be done is to sync the IDs.
           if !issue.trac_id.nil? and (issue.trac_id != ticket.id)
-            @logger.error("ERROR! Found a match by title, but the IDs don't match!!")
-            @logger.error("       issue has #{issue.trac_id}, ticket has #{ticket.id}")
-            # TODO do something useful here
+            @logger.error("Found a match by title, but the IDs don't match!!")
+            @logger.error("\tissue has #{issue.trac_id}, ticket has #{ticket.id}")
+            @logger.error("\tFixing (by sync'ing the issue's trac ID)")
+            # The fix is a few lines down
+            issue.trac_id = nil
           end
+
           @logger.info("Found ticket/issue match #{ issue.trac_id ? "by id" : "by name"}")
-          issues.delete(issue)
-          issue.trac_id = ticket.id if issue.trac_id.nil?
+          # We've paired the issue, so remove it from the unpaired list
+          unpaired_issues.delete(issue)
+          issue.trac_id = ticket.id
         end
         rv << [ticket,issue]
       end
+
       # Now put the left-over, unmatched issues in with a nil ticket
       # TODO check here that none of these issues have trac_ids.
-      for issue in issues
+      for issue in unpaired_issues
         @logger.info("Found no match for #{issue.id[0,4]}: #{issue.title}")
         rv << [nil,issue]
       end

          
@@ 192,18 207,21 @@ module Ditz
           @logger.warn("Orphaned ticket ##{t.id}: milestone #{t.milestone} already released!")
           @logger.warn("\t#{t.summary}")
         else
-          issue = Ditz::Issue.create({:reporter => t.reporter,
-                                     :creation_time => t.created_at.to_time,
-                                     :title => t.summary,
-                                     :type  => TTYPE_DTYPE[ t.type ],
-                                     :desc  => t.description,
-                                     :release => release,
-                                     :component => t.component,
-                                     :status => TSTATUS_DSTATUS[ t.status ],
-                                     :disposition => resolution,
-                                     :references => [],
-                                     :trac_id => t.id
-          }, [@config, @project])
+          release_name = release ? release.name : nil
+          attrs = {:reporter => t.reporter,
+            :creation_time => t.created_at.to_time,
+            :title => t.summary,
+            :type  => TTYPE_DTYPE[ t.type ],
+            :desc  => t.description,
+            :release => release_name,
+            :component => t.component,
+            :status => TSTATUS_DSTATUS[ t.status ],
+            :disposition => resolution,
+            :references => [],
+            :trac_id => t.id
+          }
+          @logger.debug( "Creating ticket with attributes #{attrs.inspect}" )
+          issue = Ditz::Issue.create(attrs, [@config, @project])
 
           @project.add_issue( issue )
           @logger.info("Created issue #{issue.id[0,4]}: #{issue.title}")

          
@@ 355,9 373,12 @@ module Ditz
       end
 
       cl_comments.each do |c|
-        unless c[4]=="" || c[4].nil?
+        unless c[4]=="" || c[4].nil? || (c[4] =~ /^Ticket synced from ditz/)
           @logger.info( "Updating issue #{issue.id} with comment #{c[4].inspect}" )
-          issue.log_at( c[0].to_time, "commented", c[1], c[4] )
+          
+          unless issue.log_events.find {|e| e[0] == c[0].to_time && e[2] == "commented" && e[3] == c[4] }
+            issue.log_at( c[0].to_time, "commented", c[1], c[4] )
+          end
         end
       end
 

          
@@ 365,7 386,7 @@ module Ditz
         @logger.debug( "Issue comments =>" )
         @logger.debug( el_comments.inspect )
         el_comments.each do |e|
-          unless e[3]=="" || e[3].nil?
+          unless e[3]=="" || e[3].nil? || (e[3] =~ /^Ticket synced from ditz/)
             @logger.info( "Updating ticket #{ticket.id} with comment #{e[3].inspect}" )
             try {
               @trac.query( "ticket.update", ticket.id, e[3] )

          
@@ 375,10 396,6 @@ module Ditz
       end
     end
 
-    def equal?( ticket, issue )
-      (ticket.id == issue.trac_id) || (ticket.summary == issue.title)
-    end
-
     def change_status(status, issue)
       if issue.status != status
         old_status = issue.status