diff --git a/pcbnew/router/pns_node.cpp b/pcbnew/router/pns_node.cpp
index 3889f30b54..508a180860 100644
--- a/pcbnew/router/pns_node.cpp
+++ b/pcbnew/router/pns_node.cpp
@@ -873,10 +873,10 @@ void NODE::Replace( ITEM* aOldItem, std::unique_ptr< ITEM > aNewItem )
 }
 
 
-void NODE::Replace( LINE& aOldLine, LINE& aNewLine )
+void NODE::Replace( LINE& aOldLine, LINE& aNewLine, bool aAllowRedundantSegments )
 {
     Remove( aOldLine );
-    Add( aNewLine );
+    Add( aNewLine, aAllowRedundantSegments );
 }
 
 
diff --git a/pcbnew/router/pns_node.h b/pcbnew/router/pns_node.h
index 20e5a7ccc0..377a9520b9 100644
--- a/pcbnew/router/pns_node.h
+++ b/pcbnew/router/pns_node.h
@@ -389,7 +389,7 @@ public:
      * @param aNewItem item add instead
      */
     void Replace( ITEM* aOldItem, std::unique_ptr< ITEM > aNewItem );
-    void Replace( LINE& aOldLine, LINE& aNewLine );
+    void Replace( LINE& aOldLine, LINE& aNewLine, bool aAllowRedundantSegments = false );
 
     /**
      * Create a lightweight copy (called branch) of self that tracks the changes (added/removed
diff --git a/pcbnew/router/pns_shove.cpp b/pcbnew/router/pns_shove.cpp
index 721543349e..7b05d22677 100644
--- a/pcbnew/router/pns_shove.cpp
+++ b/pcbnew/router/pns_shove.cpp
@@ -77,7 +77,7 @@ void SHOVE::replaceItems( ITEM* aOld, std::unique_ptr< ITEM > aNew )
 }
 
 
-SHOVE::ROOT_LINE_ENTRY* SHOVE::replaceLine( LINE& aOld, LINE& aNew, bool aIncludeInChangedArea, NODE* aNode )
+SHOVE::ROOT_LINE_ENTRY* SHOVE::replaceLine( LINE& aOld, LINE& aNew, bool aIncludeInChangedArea, bool aAllowRedundantSegments, NODE* aNode )
 {
     if( aIncludeInChangedArea )
     {
@@ -142,9 +142,10 @@ SHOVE::ROOT_LINE_ENTRY* SHOVE::replaceLine( LINE& aOld, LINE& aNew, bool aInclud
 
     // Now update the NODE (calling Replace invalidates the Links() in a LINE)
     if( aNode )
-        aNode->Replace( aOld, aNew );
+        aNode->Replace( aOld, aNew, aAllowRedundantSegments );
     else
-        m_currentNode->Replace( aOld, aNew );
+        m_currentNode->Replace( aOld, aNew, aAllowRedundantSegments );
+
 
     // point the Links() of the new line to its oldest ancestor
     for( LINKED_ITEM* link : aNew.Links() )
@@ -298,12 +299,11 @@ bool SHOVE::shoveLineFromLoneVia( const LINE& aCurLine, const LINE& aObstacleLin
  * Re-walk aObstacleLine around the given set of hulls, returning the result in aResultLine.
  */
 bool SHOVE::shoveLineToHullSet( const LINE& aCurLine, const LINE& aObstacleLine,
-                                               LINE& aResultLine, const HULL_SET& aHulls )
+                                               LINE& aResultLine, const HULL_SET& aHulls, bool aPermitAdjustingEndpoints )
 {
-    const SHAPE_LINE_CHAIN& obs = aObstacleLine.CLine();
-
+    const int c_ENDPOINT_ON_HULL_THRESHOLD = 10000;
     int attempt;
-
+    
     PNS_DBG( Dbg(), BeginGroup, "shove-details", 1 );
 
     for( attempt = 0; attempt < 4; attempt++ )
@@ -311,10 +311,85 @@ bool SHOVE::shoveLineToHullSet( const LINE& aCurLine, const LINE& aObstacleLine,
         bool invertTraversal = ( attempt >= 2 );
         bool clockwise = attempt % 2;
         int vFirst = -1, vLast = -1;
-
+        SHAPE_LINE_CHAIN obs = aObstacleLine.CLine();
         LINE l( aObstacleLine );
         SHAPE_LINE_CHAIN path( l.CLine() );
 
+        if( aPermitAdjustingEndpoints )
+        {
+            auto minDistP = [&]( VECTOR2I pref, int& mdist, int& minhull ) -> VECTOR2I
+            {
+                int      min_dist = std::numeric_limits<int>::max();
+                VECTOR2I nearestP;
+
+                for( int i = 0; i < (int) aHulls.size(); i++ )
+                {
+                    const SHAPE_LINE_CHAIN& hull =
+                            aHulls[invertTraversal ? aHulls.size() - 1 - i : i];
+                    int  dist;
+                    auto p = hull.NearestPoint( pref, true );
+
+                    if( hull.PointInside( pref ) )
+                        dist = 0;
+                    else
+                        dist = ( p - pref ).EuclideanNorm();
+
+                    if( dist < c_ENDPOINT_ON_HULL_THRESHOLD && dist < min_dist )
+                    {
+                        bool reject = false;
+
+                        /*for( int j = 0; j < (int) aHulls.size(); j++ )
+                        {
+                            if ( i != j && aHulls[j].PointInside( p ) )
+                            {
+                                reject = true;
+                                break;
+                            }
+                        }*/
+                        
+                        if( !reject )
+                        {
+                            min_dist = dist;
+                            nearestP = p;
+                            minhull = invertTraversal ? aHulls.size() - 1 - i : i;
+                        }
+                    }
+                }
+                mdist = min_dist;
+                return nearestP;
+            };
+
+            int      minDist0, minDist1, minhull0, minhull1 ;
+            VECTOR2I p0 = minDistP( l.CPoint( 0 ), minDist0, minhull0 );
+            VECTOR2I p1 = minDistP( l.CPoint( -1 ), minDist1, minhull1 );
+
+            PNS_DBG( Dbg(), Message, wxString::Format( "mindists : %d %d hulls %d %d\n", minDist0, minDist1, minhull0, minhull1 ) );
+
+            if( minDist1 < c_ENDPOINT_ON_HULL_THRESHOLD )
+            {
+                l.Line().SetPoint( -1, p1 );
+                obs = l.CLine();
+                path = l.CLine();
+                //PNS_DBG( Dbg(), AddPoint, p1, LIGHTRED, 200000, wxString::Format( "round-p1 dist %d besthull %d", dist, bestHull ) );
+            }
+
+            if( minDist0 < c_ENDPOINT_ON_HULL_THRESHOLD )
+            {
+                l.Line().SetPoint( 0, p0 );
+                obs = l.CLine();
+                path = l.CLine();
+                /*int dist = std::numeric_limits<int>::max();
+                    for (auto& h : hulls ) 
+                    {
+                        dist = std::min(dist, h.Distance( p0, true ));
+                    }
+                    PNS_DBG( Dbg(), AddPoint, p0, LIGHTRED, 200000, wxString::Format( "round-p0 dist %d", dist ) );*/
+            }
+        }
+
+
+        bool failWalk = false;
+
         for( int i = 0; i < (int) aHulls.size(); i++ )
         {
             const SHAPE_LINE_CHAIN& hull = aHulls[invertTraversal ? aHulls.size() - 1 - i : i];
@@ -330,14 +405,17 @@ bool SHOVE::shoveLineToHullSet( const LINE& aCurLine, const LINE& aObstacleLine,
                                                            l.CLine().Format().c_str(),
                                                            clockwise? 1 : 0) );
 
-                PNS_DBGN( Dbg(), EndGroup );
-                return false;
+                failWalk = true;
+                break;
             }
 
             path.Simplify2();
             l.SetShape( path );
         }
 
+        if( failWalk )
+            continue;
+
         for( int i = 0; i < std::min( path.PointCount(), obs.PointCount() ); i++ )
         {
             if( path.CPoint( i ) != obs.CPoint( i ) )
@@ -358,7 +436,7 @@ bool SHOVE::shoveLineToHullSet( const LINE& aCurLine, const LINE& aObstacleLine,
             }
         }
 
-        if( ( vFirst < 0 || vLast < 0 ) && !path.CompareGeometry( aObstacleLine.CLine() ) )
+        if( ( vFirst < 0 || vLast < 0 ) && !path.CompareGeometry( obs ) )
         {
             PNS_DBG( Dbg(), Message, wxString::Format( wxT( "attempt %d fail vfirst-last" ),
                                                        attempt ) );
@@ -429,7 +507,7 @@ bool SHOVE::shoveLineToHullSet( const LINE& aCurLine, const LINE& aObstacleLine,
 bool SHOVE::ShoveObstacleLine( const LINE& aCurLine, const LINE& aObstacleLine,
                                               LINE& aResultLine )
 {
-    const int cHullFailureExpansionFactor = 10;
+    const int cHullFailureExpansionFactor = 1000;
     int extraHullExpansion = 0;
 
 
@@ -469,7 +547,7 @@ bool SHOVE::ShoveObstacleLine( const LINE& aCurLine, const LINE& aObstacleLine,
                                                    m_router->GetInterface()->GetNetCode( obstacleLine.Net() ),
                                                    clearance ) );*/
 
-        for( int attempt = 0; attempt < 2; attempt++ )
+        for( int attempt = 0; attempt < 3; attempt++ )
         {
             HULL_SET hulls;
 
@@ -508,7 +586,9 @@ bool SHOVE::ShoveObstacleLine( const LINE& aCurLine, const LINE& aObstacleLine,
                 hulls.push_back( aCurLine.Via().Hull( viaClearance, obstacleLineWidth, layer ) );
             }
 
-            if (shoveLineToHullSet( aCurLine, obstacleLine, aResultLine, hulls ) )
+            bool permitMovingEndpoints = (attempt >= 2);
+
+            if (shoveLineToHullSet( aCurLine, obstacleLine, aResultLine, hulls, permitMovingEndpoints ) )
             {
                 if( obsVia )
                     aResultLine.AppendVia( *obsVia );
@@ -572,14 +652,15 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingSegment( LINE& aCurrent, SEGMENT* aObstacl
 
         shovedLine.Line().Simplify2();
 
-        sanityCheck( &obstacleLine, &shovedLine );
-        replaceLine( obstacleLine, shovedLine );
+        replaceLine( obstacleLine, shovedLine, true, false );
 
         if( !pushLineStack( shovedLine ) )
             return SH_INCOMPLETE;
+        
+        return SH_OK;
     }
 
-    return SH_OK;
+    return SH_INCOMPLETE;
 }
 
 
@@ -622,8 +703,7 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingArc( LINE& aCurrent, ARC* aObstacleArc )
         int rank = aCurrent.Rank();
         shovedLine.SetRank( rank - 1 );
 
-        sanityCheck( &obstacleLine, &shovedLine );
-        replaceLine( obstacleLine, shovedLine );
+        replaceLine( obstacleLine, shovedLine, true, false );
 
         if( !pushLineStack( shovedLine ) )
             return SH_INCOMPLETE;
@@ -657,8 +737,7 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingLine( LINE& aCurrent, LINE& aObstacle, int
             m_newHead = shovedLine;
         }
 #endif
-        sanityCheck( &aObstacle, &shovedLine );
-        replaceLine( aObstacle, shovedLine );
+        replaceLine( aObstacle, shovedLine, true, false );
 
         shovedLine.SetRank( aNextRank );
 
@@ -759,7 +838,6 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingSolid( LINE& aCurrent, ITEM* aObstacle, OB
         }
 #endif
 
-    	sanityCheck( &aCurrent, &walkaroundLine );
 
         if( !m_lineStack.empty() )
         {
@@ -786,7 +864,7 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingSolid( LINE& aCurrent, ITEM* aObstacle, OB
     if(!success)
         return SH_INCOMPLETE;
 
-    replaceLine( aCurrent, walkaroundLine );
+    replaceLine( aCurrent, walkaroundLine, true, false );
     walkaroundLine.SetRank( nextRank );
 
 
@@ -941,7 +1019,7 @@ bool SHOVE::pushSpringback( NODE* aNode, const OPT_BOX2I& aAffectedArea )
  * Push or shove a via by at least aForce.  (The via might be pushed or shoved slightly further
  * to keep it from landing on an existing joint.)
  */
-SHOVE::SHOVE_STATUS SHOVE::pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, int aNewRank )
+SHOVE::SHOVE_STATUS SHOVE::pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, int aNewRank, bool aDontUnwindStack )
 {
     LINE_PAIR_VEC draggedLines;
     VECTOR2I p0( aVia->Pos() );
@@ -1015,7 +1093,9 @@ SHOVE::SHOVE_STATUS SHOVE::pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, in
 
     VIA *v2 = pushedVia.get();
 
-    unwindLineStack( aVia );
+    if( !aDontUnwindStack )
+        unwindLineStack( aVia );
+
     replaceItems( aVia, std::move( pushedVia ) );
 
     if( draggedLines.empty() ) // stitching via? make sure the router won't forget about it
@@ -1029,7 +1109,8 @@ SHOVE::SHOVE_STATUS SHOVE::pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, in
     int n = 0;
     for( LINE_PAIR lp : draggedLines )
     {
-        unwindLineStack( &lp.first );
+        if( !aDontUnwindStack )
+            unwindLineStack( &lp.first );
 
         PNS_DBG( Dbg(), Message, wxString::Format("fan %d/%d\n", n, (int) draggedLines.size() ) );
         n++;
@@ -1037,10 +1118,12 @@ SHOVE::SHOVE_STATUS SHOVE::pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, in
         if( lp.second.SegmentCount() )
         {
             lp.second.ClearLinks();
-            ROOT_LINE_ENTRY* rootEntry = replaceLine( lp.first, lp.second );
+            ROOT_LINE_ENTRY* rootEntry = replaceLine( lp.first, lp.second, true, true );
 
             lp.second.LinkVia( v2 );
-            unwindLineStack( &lp.second );
+
+            if( !aDontUnwindStack )
+                unwindLineStack( &lp.second );
 
             lp.second.SetRank( aNewRank );
 
@@ -1273,7 +1356,7 @@ SHOVE::SHOVE_STATUS SHOVE::onReverseCollidingVia( LINE& aCurrent, VIA* aObstacle
     PNS_DBGN( Dbg(), EndGroup );
 
     int currentRank = aCurrent.Rank();
-    replaceLine( aCurrent, shoved );
+    replaceLine( aCurrent, shoved, true, false );
 
     if( !pushLineStack( shoved ) )
         return SH_INCOMPLETE;
@@ -1293,7 +1376,27 @@ void SHOVE::unwindLineStack( const LINKED_ITEM* aSeg )
         if( i->ContainsLink( aSeg ) )
         {
             PNS_DBG(Dbg(), Message, wxString::Format("Unwind lc %d (depth %d/%d)", i->SegmentCount(), d, (int)m_lineStack.size() ) );
-            i = m_lineStack.erase( i );
+//comment!
+            if( i->EndsWithVia() && !aSeg->OfKind( ITEM::VIA_T) )
+            {
+                i = m_lineStack.erase( i );
+            }
+            else
+            {
+                VIA *via = nullptr;
+
+                for( auto l : i->Links() )
+                if( l->OfKind( ITEM::VIA_T ) )
+                    via = static_cast<VIA*>( l );
+                
+                if( via )
+                {
+                    i->ClearLinks( );
+                    i->LinkVia( via );
+                }
+                i++;
+            }
+
         }
         else
         {
@@ -1347,24 +1450,29 @@ bool SHOVE::pushLineStack( const LINE& aL, bool aKeepCurrentOnTop )
         m_lineStack.push_back( aL );
     }
 
+    
+    pruneLineFromOptimizerQueue( aL );
     m_optimizerQueue.push_back( aL );
 
     return true;
 }
 
 
-void SHOVE::popLineStack( )
+bool SHOVE::pruneLineFromOptimizerQueue( const LINE& aLine )
 {
-    LINE& l = m_lineStack.back();
-
+    int pre = m_optimizerQueue.size();
     for( std::vector<LINE>::iterator i = m_optimizerQueue.begin(); i != m_optimizerQueue.end(); )
     {
         bool found = false;
 
-        for( LINKED_ITEM* s : l.Links() )
+        for( LINKED_ITEM* s : aLine.Links() )
         {
-            if( i->ContainsLink( s ) )
+            PNS_DBG( Dbg(), Message,
+                     wxString::Format( "cur lc %d lnk %p cnt %d", i->LinkCount(), s, aLine.LinkCount() ) );
+
+            if( i->ContainsLink( s ) && !s->OfKind( ITEM::VIA_T ) )
             {
+
                 i = m_optimizerQueue.erase( i );
                 found = true;
                 break;
@@ -1375,6 +1483,13 @@ void SHOVE::popLineStack( )
             i++;
     }
 
+    return true;
+}
+
+void SHOVE::popLineStack( )
+{
+    LINE& l = m_lineStack.back();
+    pruneLineFromOptimizerQueue( l );
     m_lineStack.pop_back();
 }
 
@@ -2272,11 +2387,11 @@ SHOVE::SHOVE_STATUS SHOVE::Run()
             PNS_DBG( Dbg(), Message, wxString::Format("process head-via [%d %d] node=%p", l.theVia->pos.x, l.theVia->pos.y, m_currentNode ) );
             auto realVia = m_currentNode->FindViaByHandle( *l.theVia );
             assert( realVia != nullptr );
-            headSet.Add( realVia );
+            headSet.Add( realVia->Clone() );
         }
         else
         {
-            headSet.Add( *l.origHead );
+            headSet.Add( *l.origHead->Clone() );
         }
     }
 
@@ -2324,7 +2439,7 @@ SHOVE::SHOVE_STATUS SHOVE::Run()
             viaRoot->oldVia = viaToDrag;
             headLineEntry.draggedVia = viaToDrag;
 
-            st = pushOrShoveVia( viaToDrag, ( headLineEntry.viaNewPos - viaToDrag->Pos() ), 0 );
+            st = pushOrShoveVia( viaToDrag, ( headLineEntry.viaNewPos - viaToDrag->Pos() ), 0, true );
 
             if( st != SH_OK )
                 break;
diff --git a/pcbnew/router/pns_shove.h b/pcbnew/router/pns_shove.h
index 39358d1b84..794c483731 100644
--- a/pcbnew/router/pns_shove.h
+++ b/pcbnew/router/pns_shove.h
@@ -168,8 +168,10 @@ private:
         bool m_locked;
     };
 
+    bool pruneLineFromOptimizerQueue( const LINE& aLine );
+
     bool shoveLineToHullSet( const LINE& aCurLine, const LINE& aObstacleLine,
-                                     LINE& aResultLine, const HULL_SET& aHulls );
+                                     LINE& aResultLine, const HULL_SET& aHulls, bool aPermitAdjustingEndpoints = false );
 
     NODE* reduceSpringback( const ITEM_SET& aHeadSet );
 
@@ -186,7 +188,7 @@ private:
     SHOVE_STATUS onCollidingSolid( LINE& aCurrent, ITEM* aObstacle, OBSTACLE& aObstacleInfo );
     SHOVE_STATUS onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia, OBSTACLE& aObstacleInfo, int aNextRank );
     SHOVE_STATUS onReverseCollidingVia( LINE& aCurrent, VIA* aObstacleVia, OBSTACLE& aObstacleInfo );
-    SHOVE_STATUS pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, int aNextRank );
+    SHOVE_STATUS pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, int aNextRank, bool aDontUnwindStack = false );
 
     OPT_BOX2I totalAffectedArea() const;
 
@@ -203,6 +205,7 @@ private:
     void replaceItems( ITEM* aOld, std::unique_ptr< ITEM > aNew );
     ROOT_LINE_ENTRY* replaceLine( LINE& aOld, LINE& aNew,
                     bool aIncludeInChangedArea = true,
+                    bool aAllowRedundantSegments = true,
                       NODE *aNode = nullptr );
 
     ROOT_LINE_ENTRY* findRootLine( const LINE& aLine );