1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseSVN
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
20 #include "FullGraphFinalizer.h"
\r
21 #include "FullHistory.h"
\r
22 #include "FullGraph.h"
\r
23 #include "CachedLogInfo.h"
\r
24 #include "Registry.h"
\r
25 #include "UnicodeUtils.h"
\r
28 #define new DEBUG_NEW
\r
30 static char THIS_FILE[] = __FILE__;
\r
33 CFullGraphFinalizer::CFullGraphFinalizer
\r
34 ( const CFullHistory& history
\r
35 , CFullGraph& graph)
\r
39 // initialize path classificator
\r
41 CRegStdString trunkPattern (_T("Software\\TortoiseSVN\\RevisionGraph\\TrunkPattern"), _T("trunk"));
\r
42 CRegStdString branchesPattern (_T("Software\\TortoiseSVN\\RevisionGraph\\BranchPattern"), _T("branches"));
\r
43 CRegStdString tagsPattern (_T("Software\\TortoiseSVN\\RevisionGraph\\TagsPattern"), _T("tags"));
\r
45 const CPathDictionary& paths = history.GetCache()->GetLogInfo().GetPaths();
\r
46 pathClassification.reset
\r
47 (new CPathClassificator ( paths
\r
48 , CUnicodeUtils::StdGetUTF8 (trunkPattern)
\r
49 , CUnicodeUtils::StdGetUTF8 (branchesPattern)
\r
50 , CUnicodeUtils::StdGetUTF8 (tagsPattern)));
\r
53 CFullGraphFinalizer::~CFullGraphFinalizer(void)
\r
57 void CFullGraphFinalizer::Run()
\r
59 // nothing to do for empty graphs
\r
61 if (graph.GetRoot() == NULL)
\r
64 // say "renamed" for "Deleted"/"Added" entries
\r
66 FindRenames (graph.GetRoot());
\r
68 // classify all nodes (needs to fully passes):
\r
69 // classify nodes on by one
\r
71 ForwardClassification (graph.GetRoot());
\r
73 // propagate classifation back along copy history
\r
75 BackwardClassification (graph.GetRoot());
\r
78 void CFullGraphFinalizer::FindRenames (CFullGraphNode* node)
\r
80 // say "renamed" for "Deleted"/"Added" entries
\r
82 for ( CFullGraphNode * next = node->GetNext()
\r
84 ; node = next, next = (next == NULL ? NULL : next->GetNext()))
\r
86 if ( (next != NULL)
\r
87 && (next->GetClassification().Is (CNodeClassification::IS_DELETED)))
\r
89 // this line will be deleted.
\r
90 // will it be continued exactly once under a different name?
\r
92 CFullGraphNode* renameTarget = NULL;
\r
93 CFullGraphNode::CCopyTarget** renameCopy = NULL;
\r
95 for ( CFullGraphNode::CCopyTarget** copy = &node->GetFirstCopyTarget()
\r
97 ; copy = &(*copy)->next())
\r
99 CFullGraphNode * target = (*copy)->value();
\r
100 assert (target->GetClassification().Is (CNodeClassification::IS_COPY_TARGET));
\r
102 if (target->GetRevision() == next->GetRevision())
\r
104 // that actually looks like a rename
\r
106 if (renameTarget != NULL)
\r
108 // there is more than one copy target
\r
109 // -> display all individual deletion and additions
\r
111 renameTarget = NULL;
\r
116 // remember the (potential) rename target
\r
118 renameTarget = target;
\r
124 // did we find a unambigous rename target?
\r
126 if (renameTarget != NULL)
\r
130 graph.Replace ( node->GetNext()
\r
132 , CNodeClassification::IS_RENAMED);
\r
134 // "next" has just been destroyed
\r
136 next = node->GetNext();
\r
142 for ( const CFullGraphNode::CCopyTarget* copy = node->GetFirstCopyTarget()
\r
144 ; copy = copy->next())
\r
146 FindRenames (copy->value());
\r
151 // mark nodes according to local properties
\r
153 void CFullGraphFinalizer::MarkRoot (CFullGraphNode* node)
\r
155 if (node == graph.GetRoot())
\r
156 node->AddClassification (CNodeClassification::IS_FIRST);
\r
159 void CFullGraphFinalizer::MarkCopySource (CFullGraphNode* node)
\r
161 if (node->GetFirstCopyTarget() != NULL)
\r
162 node->AddClassification (CNodeClassification::IS_COPY_SOURCE);
\r
165 void CFullGraphFinalizer::MarkWCRevision (CFullGraphNode* node)
\r
167 // if this the same revision and path as the WC?
\r
169 if ( (node->GetRevision() == history.GetWCRevision())
\r
170 && (node->GetPath().GetBasePath().Intersects
\r
171 (history.GetWCPath()->GetBasePath())))
\r
173 node->AddClassification (CNodeClassification::IS_WORKINGCOPY);
\r
177 void CFullGraphFinalizer::MarkHead (CFullGraphNode* node)
\r
179 // scan all "latest" nodes
\r
180 // (they must be either HEADs or special nodes)
\r
182 if ( (node->GetNext() != NULL)
\r
183 || (node->GetClassification().IsAnyOf
\r
184 (CNodeClassification::SUBTREE_DELETED)))
\r
187 // look for the latest change
\r
188 // (there may be several "copy-source-only" nodes trailing HEAD
\r
190 while (node->GetClassification().Matches (0, CNodeClassification::IS_OPERATION_MASK))
\r
191 node = node->GetPrevious();
\r
193 node->AddClassification (CNodeClassification::IS_LAST);
\r
196 // classify nodes on by one
\r
198 void CFullGraphFinalizer::ForwardClassification (CFullGraphNode* node)
\r
202 // add local classification
\r
205 MarkCopySource (node);
\r
206 MarkWCRevision (node);
\r
209 // add path-based classification
\r
211 node->AddClassification ((*pathClassification)[node->GetPath()]);
\r
215 for ( const CFullGraphNode::CCopyTarget* copy = node->GetFirstCopyTarget()
\r
217 ; copy = copy->next())
\r
219 ForwardClassification (copy->value());
\r
222 node = node->GetNext();
\r
224 while (node != NULL);
\r
227 // propagate classifation back along copy history
\r
229 DWORD CFullGraphFinalizer::BackwardClassification (CFullGraphNode* node)
\r
231 // start at the end of this chain
\r
233 assert (node->GetPrevious()== NULL);
\r
235 while (node->GetNext())
\r
236 node = node->GetNext();
\r
238 // classify this branch
\r
240 DWORD branchClassification = 0;
\r
244 // set classification on copies first
\r
246 DWORD commonCopyClassfication = (DWORD)(-1); // flags set in all copyies
\r
247 DWORD aggregatedCopyClassification = 0; // flags set in at least one copy
\r
249 for ( const CFullGraphNode::CCopyTarget* copy = node->GetFirstCopyTarget()
\r
251 ; copy = copy->next())
\r
253 DWORD classification = BackwardClassification (copy->value());
\r
254 commonCopyClassfication &= classification;
\r
255 aggregatedCopyClassification |= classification;
\r
258 // construct the common classification
\r
260 DWORD classification // aggregate changes along the branch
\r
261 = branchClassification
\r
262 & ~CNodeClassification::ALL_COPIES_MASK;
\r
265 |= (node->GetClassification().GetFlags() * CNodeClassification::PATH_ONLY_SHIFT)
\r
266 & CNodeClassification::PATH_ONLY_MASK;
\r
268 classification // add what applies to all branches
\r
269 |= commonCopyClassfication & branchClassification
\r
270 & CNodeClassification::ALL_COPIES_MASK;
\r
272 classification // any change to this node applies to all copies as well
\r
273 |= (node->GetClassification().GetFlags() * CNodeClassification::ALL_COPIES_SHIFT)
\r
274 & commonCopyClassfication
\r
275 & CNodeClassification::ALL_COPIES_MASK;
\r
277 classification // add changes that occur in *any* sub-tree
\r
278 |= (aggregatedCopyClassification * CNodeClassification::COPIES_TO_SHIFT)
\r
279 & CNodeClassification::COPIES_TO_MASK;
\r
281 // store and return the flags
\r
283 DWORD nodeClassification
\r
285 & ( CNodeClassification::ALL_COPIES_MASK
\r
286 + CNodeClassification::COPIES_TO_MASK
\r
287 + CNodeClassification::PATH_ONLY_MASK);
\r
289 node->AddClassification (nodeClassification);
\r
291 // current path classification
\r
293 branchClassification = classification | node->GetClassification().GetFlags();
\r
294 node = node->GetPrevious();
\r
296 while (node != NULL);
\r
300 return branchClassification;
\r