GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/publish/cmd_diff.cc
Date: 2026-05-03 02:36:16
Exec Total Coverage
Lines: 0 132 0.0%
Branches: 0 420 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5
6 #include "cmd_diff.h"
7
8 #include <inttypes.h>
9
10 #include <cassert>
11 #include <string>
12 #include <vector>
13
14 #include "catalog_counters.h"
15 #include "directory_entry.h"
16 #include "history.h"
17 #include "publish/repository.h"
18 #include "publish/settings.h"
19 #include "util/logging.h"
20 #include "util/pointer.h"
21 #include "util/string.h"
22
23 namespace {
24
25 class DiffReporter : public publish::DiffListener {
26 public:
27 DiffReporter(bool show_header, bool machine_readable, bool ignore_timediff)
28 : show_header_(show_header)
29 , machine_readable_(machine_readable)
30 , ignore_timediff_(ignore_timediff) { }
31 virtual ~DiffReporter() { }
32
33
34 virtual void OnInit(const history::History::Tag &from_tag,
35 const history::History::Tag &to_tag) {
36 if (!show_header_)
37 return;
38
39 if (machine_readable_) {
40 LogCvmfs(
41 kLogCvmfs, kLogStdout,
42 "# line descriptor: A - add, R - remove, M - modify, "
43 "S - statistics; modify flags: S - size, M - mode, T - timestamp, "
44 "C - content, L - symlink target; entry types: F - regular file, "
45 "S - symbolic link, D - directory, N - nested catalog");
46 } else {
47 LogCvmfs(kLogCvmfs, kLogStdout,
48 "DELTA: %s/r%" PRIu64 " (%s) --> %s/r%" PRIu64 " (%s)",
49 from_tag.name.c_str(), from_tag.revision,
50 StringifyTime(from_tag.timestamp, true).c_str(),
51 to_tag.name.c_str(), to_tag.revision,
52 StringifyTime(to_tag.timestamp, true).c_str());
53 }
54 }
55
56
57 virtual void OnStats(const catalog::DeltaCounters &delta) {
58 const std::string operation = machine_readable_ ? "S " : "d(";
59 const std::string type_file = machine_readable_ ? "F" : "# regular files):";
60 const std::string type_symlink = machine_readable_ ? "S" : "# symlinks):";
61 const std::string type_directory = machine_readable_ ? "D"
62 : "# directories):";
63 const std::string type_catalog = machine_readable_ ? "N" : "# catalogs):";
64 const int64_t diff_file = delta.self.regular_files
65 + delta.subtree.regular_files;
66 const int64_t diff_symlink = delta.self.symlinks + delta.subtree.symlinks;
67 const int64_t diff_catalog = delta.self.nested_catalogs
68 + delta.subtree.nested_catalogs;
69 // Nested catalogs make internally two directory entries
70 const int64_t diff_directory = delta.self.directories
71 + delta.subtree.directories - diff_catalog;
72 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s %" PRId64, operation.c_str(),
73 type_file.c_str(), diff_file);
74 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s %" PRId64, operation.c_str(),
75 type_symlink.c_str(), diff_symlink);
76 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s %" PRId64, operation.c_str(),
77 type_directory.c_str(), diff_directory);
78 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s %" PRId64, operation.c_str(),
79 type_catalog.c_str(), diff_catalog);
80 }
81
82
83 virtual void OnAdd(const std::string &path,
84 const catalog::DirectoryEntry &entry) {
85 const std::string operation = machine_readable_ ? "A" : "add";
86 if (machine_readable_) {
87 LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak, "%s %s %s",
88 operation.c_str(), PrintEntryType(entry).c_str(), path.c_str());
89 if (!entry.IsDirectory()) {
90 LogCvmfs(kLogCvmfs, kLogStdout, " +%" PRIu64, entry.size());
91 } else {
92 LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak, "\n");
93 }
94 } else {
95 LogCvmfs(kLogCvmfs, kLogStdout, "%s %s %s +%" PRIu64 " bytes",
96 path.c_str(), operation.c_str(), PrintEntryType(entry).c_str(),
97 entry.size());
98 }
99 }
100
101
102 virtual void OnRemove(const std::string &path,
103 const catalog::DirectoryEntry &entry) {
104 const std::string operation = machine_readable_ ? "R" : "remove";
105 if (machine_readable_) {
106 LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak, "%s %s %s",
107 operation.c_str(), PrintEntryType(entry).c_str(), path.c_str());
108 if (!entry.IsDirectory()) {
109 LogCvmfs(kLogCvmfs, kLogStdout, " -%" PRIu64, entry.size());
110 } else {
111 LogCvmfs(kLogCvmfs, kLogStdout, "");
112 }
113 } else {
114 LogCvmfs(kLogCvmfs, kLogStdout, "%s %s %s -%" PRIu64 " bytes",
115 path.c_str(), operation.c_str(), PrintEntryType(entry).c_str(),
116 entry.size());
117 }
118 }
119
120
121 virtual void OnModify(const std::string &path,
122 const catalog::DirectoryEntry &entry_from,
123 const catalog::DirectoryEntry &entry_to) {
124 catalog::DirectoryEntryBase::Differences diff = entry_from.CompareTo(
125 entry_to);
126 if (ignore_timediff_) {
127 diff = diff & ~catalog::DirectoryEntryBase::Difference::kMtime;
128 if (diff == 0)
129 return;
130 }
131 if (entry_from.IsDirectory() || entry_to.IsDirectory()) {
132 diff = diff & ~catalog::DirectoryEntryBase::Difference::kSize;
133 if (diff == 0)
134 return;
135 }
136
137 const std::string type_from = PrintEntryType(entry_from);
138 const std::string type_to = PrintEntryType(entry_to);
139 std::string type = type_from;
140 if (type_from != type_to) {
141 type += machine_readable_ ? type_to : ("->" + type_to);
142 }
143
144 const std::string operation = machine_readable_ ? "M" : "modify";
145 if (machine_readable_) {
146 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s %s %s", operation.c_str(),
147 PrintDifferences(diff).c_str(), type.c_str(), path.c_str());
148 } else {
149 LogCvmfs(kLogCvmfs, kLogStdout, "%s %s %s%s", path.c_str(),
150 operation.c_str(), type.c_str(), PrintDifferences(diff).c_str());
151 }
152 }
153
154 private:
155 std::string PrintDifferences(catalog::DirectoryEntryBase::Differences diff) {
156 std::vector<std::string> result_list;
157 if (diff & catalog::DirectoryEntryBase::Difference::kName)
158 result_list.push_back(machine_readable_ ? "N" : "name");
159 if (diff & catalog::DirectoryEntryBase::Difference::kLinkcount)
160 result_list.push_back(machine_readable_ ? "I" : "link-count");
161 if (diff & catalog::DirectoryEntryBase::Difference::kSize)
162 result_list.push_back(machine_readable_ ? "S" : "size");
163 if (diff & catalog::DirectoryEntryBase::Difference::kMode)
164 result_list.push_back(machine_readable_ ? "M" : "mode");
165 if (diff & catalog::DirectoryEntryBase::Difference::kMtime)
166 result_list.push_back(machine_readable_ ? "T" : "timestamp");
167 if (diff & catalog::DirectoryEntryBase::Difference::kSymlink)
168 result_list.push_back(machine_readable_ ? "L" : "symlink-target");
169 if (diff & catalog::DirectoryEntryBase::Difference::kChecksum)
170 result_list.push_back(machine_readable_ ? "C" : "content");
171 if (diff & catalog::DirectoryEntryBase::Difference::kHardlinkGroup)
172 result_list.push_back(machine_readable_ ? "G" : "hardlink-group");
173 if (diff
174 & catalog::DirectoryEntryBase::Difference::
175 kNestedCatalogTransitionFlags) {
176 result_list.push_back(machine_readable_ ? "N" : "nested-catalog");
177 }
178 if (diff & catalog::DirectoryEntryBase::Difference::kChunkedFileFlag)
179 result_list.push_back(machine_readable_ ? "P" : "chunked-file");
180 if (diff & catalog::DirectoryEntryBase::Difference::kHasXattrsFlag)
181 result_list.push_back(machine_readable_ ? "X" : "xattrs");
182 if (diff & catalog::DirectoryEntryBase::Difference::kExternalFileFlag)
183 result_list.push_back(machine_readable_ ? "E" : "external-file");
184 if (diff & catalog::DirectoryEntryBase::Difference::kBindMountpointFlag)
185 result_list.push_back(machine_readable_ ? "B" : "bind-mountpoint");
186 if (diff & catalog::DirectoryEntryBase::Difference::kHiddenFlag)
187 result_list.push_back(machine_readable_ ? "H" : "hidden");
188 if (diff & catalog::DirectoryEntryBase::Difference::kDirectIoFlag)
189 result_list.push_back(machine_readable_ ? "D" : "direct-io");
190 if (diff & catalog::DirectoryEntryBase::Difference::kUid)
191 result_list.push_back(machine_readable_ ? "U" : "uid");
192 if (diff & catalog::DirectoryEntryBase::Difference::kGid)
193 result_list.push_back(machine_readable_ ? "R" : "gid");
194
195 return machine_readable_ ? ("[" + JoinStrings(result_list, "") + "]")
196 : (" [" + JoinStrings(result_list, ", ") + "]");
197 }
198
199 std::string PrintEntryType(const catalog::DirectoryEntry &entry) {
200 if (entry.IsRegular())
201 return machine_readable_ ? "F" : "file";
202 else if (entry.IsLink())
203 return machine_readable_ ? "S" : "symlink";
204 else if (entry.IsDirectory())
205 return machine_readable_ ? "D" : "directory";
206 else
207 return machine_readable_ ? "U" : "unknown";
208 }
209
210 bool show_header_;
211 bool machine_readable_;
212 bool ignore_timediff_;
213 }; // class DiffReporter
214
215 } // anonymous namespace
216
217
218 namespace publish {
219
220 int CmdDiff::Main(const Options &options) {
221 SettingsBuilder builder;
222
223 if (options.Has("worktree")) {
224 const UniquePtr<SettingsPublisher> settings(builder.CreateSettingsPublisher(
225 options.plain_args().empty() ? "" : options.plain_args()[0].value_str));
226 settings->SetIsSilent(true);
227 settings->GetTransaction()->SetDryRun(true);
228 settings->GetTransaction()->SetPrintChangeset(true);
229 Publisher publisher(*settings);
230 publisher.Sync();
231 return 0;
232 }
233
234 SettingsRepository settings = builder.CreateSettingsRepository(
235 options.plain_args().empty() ? "" : options.plain_args()[0].value_str);
236
237 const std::string from = options.GetStringDefault("from", "trunk-previous");
238 const std::string to = options.GetStringDefault("to", "trunk");
239
240 if (options.Has("keychain")) {
241 settings.GetKeychain()->SetKeychainDir(options.GetString("keychain"));
242 }
243 Repository repository(settings);
244
245 DiffReporter diff_reporter(options.Has("header"),
246 options.Has("machine-readable"),
247 options.Has("ignore-timediff"));
248 repository.Diff(from, to, &diff_reporter);
249
250 return 0;
251 }
252
253 } // namespace publish
254