mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
Bug #887
- Fixed circular dependency detection by implementing a basic DFS Signed-off-by: Paul Beckingham <paul@beckingham.net>
This commit is contained in:
parent
30a97f5f52
commit
726fc33fe4
2 changed files with 29 additions and 43 deletions
2
AUTHORS
2
AUTHORS
|
@ -62,6 +62,7 @@ The following submitted code, packages or analysis, and deserve special thanks:
|
||||||
Greg Grossmeier
|
Greg Grossmeier
|
||||||
Barton Meeks
|
Barton Meeks
|
||||||
Martin Klepsch
|
Martin Klepsch
|
||||||
|
Wim Schuermann
|
||||||
|
|
||||||
Thanks to the following, who submitted detailed bug reports and excellent
|
Thanks to the following, who submitted detailed bug reports and excellent
|
||||||
suggestions:
|
suggestions:
|
||||||
|
@ -105,7 +106,6 @@ suggestions:
|
||||||
Jonathan Hankins
|
Jonathan Hankins
|
||||||
Andreas Kalex
|
Andreas Kalex
|
||||||
Adam Wolk
|
Adam Wolk
|
||||||
Wim Schuermann
|
|
||||||
Tom Duffy
|
Tom Duffy
|
||||||
Miguel de Val Borro
|
Miguel de Val Borro
|
||||||
Yann Davin
|
Yann Davin
|
||||||
|
|
|
@ -25,12 +25,12 @@
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
#define L10N // Localization complete.
|
#define L10N // Localization complete.
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <stack>
|
||||||
#include <Context.h>
|
#include <Context.h>
|
||||||
#include <text.h>
|
#include <text.h>
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
|
@ -39,9 +39,6 @@
|
||||||
|
|
||||||
extern Context context;
|
extern Context context;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
static bool followUpstream (const Task&, const Task&, std::vector <std::string>&);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// A task is blocked if it depends on tasks that are pending or waiting.
|
// A task is blocked if it depends on tasks that are pending or waiting.
|
||||||
//
|
//
|
||||||
|
@ -116,49 +113,38 @@ void dependencyGetBlocking (const Task& task, std::vector <Task>& blocking)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Terminology:
|
// Returns true if the supplied task adds a cycle to the dependency chain.
|
||||||
// --> if a depends on b, then it can be said that a --> b
|
|
||||||
// Head if a --> b, then b is the head
|
|
||||||
// Tail if a --> b, then a is the tail
|
|
||||||
//
|
|
||||||
// Algorithm:
|
|
||||||
// Keep walking the chain, recording the links (a --> b, b --> c, ...) until
|
|
||||||
// either the end of the chain is found (therefore not circular), or the chain
|
|
||||||
// loops and a repeat link is spotted (therefore circular).
|
|
||||||
bool dependencyIsCircular (const Task& task)
|
bool dependencyIsCircular (const Task& task)
|
||||||
{
|
{
|
||||||
std::vector <std::string> seen;
|
std::stack <Task> s;
|
||||||
return followUpstream (task, task, seen);
|
std::vector <int> deps_current;
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
std::string task_uuid = task.get ("uuid");
|
||||||
// Returns true if a task is encountered twice in a chain walk, and therefore
|
|
||||||
// indicates circularity. Recursive.
|
s.push (task);
|
||||||
static bool followUpstream (
|
while (!s.empty ())
|
||||||
const Task& task,
|
|
||||||
const Task& original,
|
|
||||||
std::vector <std::string>& seen)
|
|
||||||
{
|
|
||||||
std::vector <Task> blocking;
|
|
||||||
dependencyGetBlocking (task, blocking);
|
|
||||||
std::vector <Task>::iterator b;
|
|
||||||
for (b = blocking.begin (); b != blocking.end (); ++b)
|
|
||||||
{
|
{
|
||||||
std::string link = task.get ("uuid") + " -> " + b->get ("uuid");
|
Task& current = s.top ();
|
||||||
|
current.getDependencies (deps_current);
|
||||||
|
|
||||||
// Have we seen this link before? If so, circularity has been detected.
|
// This is a basic depth first search that always terminates given the
|
||||||
if (std::find (seen.begin (), seen.end (), link) != seen.end ())
|
// assumption that any cycles in the dependency graph must have been
|
||||||
|
// introduced by the task that is being checked.
|
||||||
|
// Since any previous cycles would have been prevented by this very
|
||||||
|
// function, this is a reasonable assumption.
|
||||||
|
for (unsigned int i = 0; i < deps_current.size (); i++)
|
||||||
|
{
|
||||||
|
context.tdb2.get (deps_current[i], current);
|
||||||
|
|
||||||
|
if (task_uuid == current.get ("uuid"))
|
||||||
|
{
|
||||||
|
// Cycle found, initial task reached for the second time!
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
seen.push_back (link);
|
s.push (current);
|
||||||
|
}
|
||||||
// Use 'original' over '*b' if they both refer to the same task, otherwise
|
s.pop ();
|
||||||
// '*b' is from TDB2's committed list, and lacks recent modifications.
|
|
||||||
if (followUpstream (
|
|
||||||
(b->get ("uuid") == original.get ("uuid") ? original : *b),
|
|
||||||
original,
|
|
||||||
seen))
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue