-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRepository.cpp
237 lines (194 loc) · 7.67 KB
/
Repository.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#include "includes/Repository.h"
#include "includes/File.h"
#include <fstream>
#include <iostream>
#include <optional>
#include <experimental/filesystem>
#include <vector>
#include <chrono>
#include <ctime>
namespace fs = std::experimental::filesystem;
/**
* @brief Constructs a Repository object with the specified repository path.
* @param repoPath The path to the repository.
*/
Repository::Repository(const std::string& repoPath) : repoPath(repoPath) {
gitFolderPath = repoPath + "/git";
logFilePath = gitFolderPath + "/log";
filesPath = gitFolderPath + "/files";
loadCommittedFiles();
}
/**
* @brief Adds a file to the list of committed files.
* @param fileName The name of the file to add.
* @return A message indicating the add status.
*/
std::string Repository::add(const std::string& fileName) {
if (std::ifstream(repoPath + "/" + fileName)) {
auto it = committedFiles.find(fileName);
if (it == committedFiles.end()) {
committedFiles[fileName] = std::nullopt;
log(Operation::Add, "File added successfully");
std::cout << "Added: " << fileName << std::endl;
return "Added: " + fileName;
} else {
log(Operation::Add, "File already added to the repository");
std::cout << "File already added to the repository: " << fileName << std::endl;
return "File already added to the repository: " + fileName;
}
} else {
log(Operation::Add, "File does not exist in the repository");
std::cout << "File does not exist in the repository. Cannot add." << std::endl;
return "File does not exist in the repository. Cannot add.";
}
}
/**
* @brief Commits changes to a file in the repository.
* @param fileName The name of the file to commit.
* @param message The commit message.
* @return A message indicating the commit status.
*/
std::string Repository::commit(const std::string& fileName, const std::string& message) {
if (std::ifstream(repoPath + "/" + fileName)) {
auto it = committedFiles.find(fileName);
if (it != committedFiles.end()) {
File file(repoPath + "/" + fileName);
file.calculateHash();
std::optional<size_t> currentHash = file.getHash();
if (!it->second.has_value() || currentHash != it->second.value()) {
committedFiles[fileName] = currentHash;
saveCommittedFiles(message);
std::cout << "Committed changes for file: " << fileName << std::endl;
return "Committed changes for file: " + fileName;
} else {
log(Operation::Commit, "No changes to commit for file");
std::cout << "No changes to commit for file: " << fileName << std::endl;
return "No changes to commit for file: " + fileName;
}
} else {
log(Operation::Commit, "File not found in committed files");
std::cout << "File not found in committed files. Add the file first." << std::endl;
return "File not found in committed files. Add the file first.";
}
} else {
log(Operation::Commit, "File does not exist in the repository");
std::cout << "File does not exist in the repository. Cannot commit." << std::endl;
return "File does not exist in the repository. Cannot commit.";
}
}
/**
* @brief Logs an operation with a message.
* @param operation The type of operation (Init, Add, Commit, etc.).
* @param message The log message.
*/
void Repository::log(Operation operation, const std::string& message) const {
std::ofstream logFile(logFilePath, std::ios::app);
if (!logFile.is_open()) {
std::cerr << "Error opening log file: " << logFilePath << std::endl;
return;
}
std::string operationStr;
switch (operation) {
case Operation::Add:
operationStr = "Add";
break;
case Operation::Commit:
operationStr = "Commit";
break;
}
auto now = std::chrono::system_clock::now();
auto now_c = std::chrono::system_clock::to_time_t(now);
std::tm* now_tm = std::localtime(&now_c);
char buffer[80];
std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", now_tm);
logFile << std::string(buffer) << " " << operationStr << ": " << message << std::endl;
logFile << "--------------------------------" << std::endl;
logFile.close();
}
/**
* @brief Loads information about committed files from the filesPath.
*/
void Repository::loadCommittedFiles() {
std::ifstream filesFile(filesPath);
std::string fileName;
size_t hash;
while (filesFile >> fileName >> hash) {
committedFiles[fileName] = hash;
}
}
/**
* @brief Overwrites the filesPath file with committed files information.
*/
void Repository::overwriteFilesFile() const {
std::ofstream filesFile(filesPath, std::ios::trunc);
if (!filesFile.is_open()) {
std::cerr << "Error opening file for overwriting: " << filesPath << std::endl;
return;
}
for (const auto& entry : committedFiles) {
if (entry.second.has_value()) {
filesFile << entry.first << " " << entry.second.value() << std::endl;
}
}
filesFile.close();
}
/**
* @brief Saves committed files information to the filesPath and logs a commit message.
* @param message The commit message.
*/
void Repository::saveCommittedFiles(const std::string& message) const {
overwriteFilesFile();
log(Operation::Commit, message);
}
/**
* @brief Gets the status of the repository.
* @return A vector of strings indicating the status of files in the repository.
*/
std::vector<std::string> Repository::status() const {
std::vector<std::string> v;
std::cout << "Repository Status:" << std::endl;
for (const auto& file : fs::directory_iterator(repoPath)) {
std::string fileName = file.path().filename().string();
if (fileName != "git") {
auto it = committedFiles.find(fileName);
if (it == committedFiles.end()) {
v.push_back(fileName + " is untracked.");
std::cout << fileName << " is untracked." << std::endl;
} else {
File file(repoPath + "/" + fileName);
auto currentHash = file.getHash();
if (!it->second.has_value()) {
v.push_back(fileName + " added but not committed.");
std::cout << fileName << " added but not committed." << std::endl;
} else if (currentHash != it->second.value()) {
v.push_back(fileName + " has been modified.");
std::cout << fileName << " has been modified." << std::endl;
} else {
v.push_back(fileName + " is up to date.");
std::cout << fileName << " is up to date." << std::endl;
}
}
}
}
std::cout << "-------------------" << std::endl;
return v;
}
/**
* @brief Initializes the repository.
* @return A message indicating the initialization status.
*/
std::string Repository::init() {
std::string gitFolderPath = repoPath + "/git";
if (fs::exists(gitFolderPath)) {
std::cout << "Repository already initialized at " << repoPath << std::endl;
return "Opened repository at: " + repoPath;
} else {
fs::create_directories(gitFolderPath);
std::ofstream logFile(gitFolderPath + "/log");
logFile.close();
std::ofstream filesFile(gitFolderPath + "/Files");
filesFile.close();
std::cout << "Initialized empty repository in " << repoPath << std::endl;
return "Initialized empty repository in " + repoPath;
}
}