• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GothenburgBitFactory / taskwarrior / 9929113862

14 Jul 2024 03:59PM UTC coverage: 84.28% (-0.07%) from 84.35%
9929113862

push

github

web-flow
Restore 'task purge' functionality (#3540)

Co-authored-by: ryneeverett <ryneeverett@gmail.com>

69 of 74 new or added lines in 3 files covered. (93.24%)

28 existing lines in 2 files now uncovered.

19327 of 22932 relevant lines covered (84.28%)

20319.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.0
/src/commands/CmdPurge.cpp
1
////////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
4
//
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
6
// of this software and associated documentation files (the "Software"), to deal
7
// in the Software without restriction, including without limitation the rights
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the Software is
10
// furnished to do so, subject to the following conditions:
11
//
12
// The above copyright notice and this permission notice shall be included
13
// in all copies or substantial portions of the Software.
14
//
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
// SOFTWARE.
22
//
23
// https://www.opensource.org/licenses/mit-license.php
24
//
25
////////////////////////////////////////////////////////////////////////////////
26

27
#include <cmake.h>
28
#include <CmdPurge.h>
29
#include <Context.h>
30
#include <Filter.h>
31
#include <main.h>
32
#include <format.h>
33
#include <shared.h>
34

35
////////////////////////////////////////////////////////////////////////////////
36
CmdPurge::CmdPurge ()
4,381✔
37
{
38
  _keyword               = "purge";
4,381✔
39
  _usage                 = "task <filter> purge";
4,381✔
40
  _description           = "Removes the specified tasks from the data files. Causes permanent loss of data.";
4,381✔
41
  _read_only             = false;
4,381✔
42
  _displays_id           = false;
4,381✔
43
  _needs_confirm         = true;
4,381✔
44
  _needs_gc              = true;
4,381✔
45
  _uses_context          = true;
4,381✔
46
  _accepts_filter        = true;
4,381✔
47
  _accepts_modifications = false;
4,381✔
48
  _accepts_miscellaneous = false;
4,381✔
49
  _category              = Command::Category::operation;
4,381✔
50
}
4,381✔
51

52
////////////////////////////////////////////////////////////////////////////////
53
// Purges the task, while taking care of:
54
// - dependencies on this task
55
// - child tasks
56
void CmdPurge::handleRelations (Task& task, std::vector<Task>& tasks)
8✔
57
{
58
  handleDeps (task);
8✔
59
  handleChildren (task, tasks);
8✔
60
  tasks.push_back(task);
6✔
61
}
6✔
62

63
////////////////////////////////////////////////////////////////////////////////
64
// Makes sure that any task having the dependency on the task being purged
65
// has that dependency removed, to preserve referential integrity.
66
void CmdPurge::handleDeps (Task& task)
8✔
67
{
68
   std::string uuid = task.get ("uuid");
16✔
69

70
   for (auto& blockedConst: Context::getContext ().tdb2.all_tasks ())
39✔
71
   {
72
     Task& blocked = const_cast<Task&>(blockedConst);
31✔
73
     if (blocked.hasDependency (uuid))
31✔
74
     {
75
         blocked.removeDependency (uuid);
2✔
76
         Context::getContext ().tdb2.modify (blocked);
2✔
77
     }
78
   }
8✔
79
}
8✔
80

81
////////////////////////////////////////////////////////////////////////////////
82
// Makes sure that with any recurrence parent are all the child tasks removed
83
// as well. If user chooses not to, the whole command is aborted.
84
void CmdPurge::handleChildren (Task& task, std::vector<Task>& tasks)
8✔
85
{
86
  // If this is not a recurrence parent, we have no job here
87
  if (!task.has ("mask"))
8✔
88
    return;
5✔
89

90
  std::string uuid = task.get ("uuid");
6✔
91
  std::vector<Task> children;
3✔
92

93
  // Find all child tasks
94
  for (auto& childConst: Context::getContext ().tdb2.all_tasks ())
12✔
95
  {
96
    Task& child = const_cast<Task&> (childConst);
10✔
97

98
    if (child.get ("parent") == uuid)
10✔
99
    {
100
      if (child.getStatus () != Task::deleted)
7✔
101
        // In case any child task is not deleted, bail out
102
        throw format ("Task '{1}' is a recurrence template. Its child task {2} must be deleted before it can be purged.",
1✔
103
                      task.get ("description"),
2✔
104
                      child.identifier (true));
2✔
105
      else
106
        children.push_back (child);
6✔
107
    }
108
  }
3✔
109

110
  // If there are no children, our job is done
111
  if (children.empty ())
2✔
NEW
112
    return;
×
113

114
  // Ask for confirmation to purge them, if needed
115
  std::string question = format ("Task '{1}' is a recurrence template. All its {2} deleted children tasks will be purged as well. Continue?",
116
                                 task.get ("description"),
4✔
117
                                 children.size ());
6✔
118

119
  if (Context::getContext ().config.getBoolean ("recurrence.confirmation") ||
8✔
120
      (Context::getContext ().config.get ("recurrence.confirmation") == "prompt"
4✔
121
       && confirm (question)))
2✔
122
  {
123
    for (auto& child: children)
4✔
124
      handleRelations (child, tasks);
3✔
125
  }
126
  else
127
    throw std::string ("Purge operation aborted.");
1✔
128
}
6✔
129

130

131
////////////////////////////////////////////////////////////////////////////////
132
int CmdPurge::execute (std::string&)
5✔
133
{
134
  int rc = 0;
5✔
135
  std::vector<Task> tasks;
5✔
136
  bool matched_deleted = false;
5✔
137

138
  Filter filter;
5✔
139
  std::vector <Task> filtered;
5✔
140

141
  // Apply filter.
142
  filter.subset (filtered);
5✔
143
  if (filtered.size () == 0)
5✔
144
  {
NEW
145
    Context::getContext ().footnote ("No tasks specified.");
×
NEW
146
    return 1;
×
147
  }
148

149
  for (auto& task : filtered)
8✔
150
  {
151
    // Allow purging of deleted tasks only. Hence no need to deal with:
152
    // - unblocked tasks notifications (deleted tasks are not blocking)
153
    // - project changes (deleted tasks not included in progress)
154
    // It also has the nice property of being explicit - users need to
155
    // mark tasks as deleted before purging.
156
    if (task.getStatus () == Task::deleted)
5✔
157
    {
158
      // Mark that at least one deleted task matched the filter
159
      matched_deleted = true;
5✔
160

161
      std::string question;
5✔
162
      question = format ("Permanently remove task {1} '{2}'?",
10✔
163
                         task.identifier (true),
10✔
164
                         task.get ("description"));
15✔
165

166
      if (permission (question, filtered.size ()))
5✔
167
        handleRelations (task, tasks);
5✔
168
    }
5✔
169
  }
170

171
  // Now that any exceptions are handled, actually purge the tasks.
172
  for (auto& task: tasks) {
9✔
173
    Context::getContext ().tdb2.purge (task);
6✔
174
  }
175

176
  if (filtered.size () > 0 and ! matched_deleted)
3✔
NEW
177
    Context::getContext ().footnote ("No deleted tasks specified. Maybe you forgot to delete tasks first?");
×
178

179
  feedback_affected (tasks.size() == 1 ? "Purged {1} task." : "Purged {1} tasks.", tasks.size());
3✔
180
  return rc;
3✔
181
}
7✔
182

183
////////////////////////////////////////////////////////////////////////////////
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc