GCC Code Coverage Report


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