summaryrefslogtreecommitdiff
path: root/gentoobrowse-api/service/maintenanceChangeLogs.cpp
blob: 027429e45c9bcf45732c20660250add3c736b27c (plain)
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
#include "maintenanceimpl.h"
#include <Ice/ObjectAdapter.h>
#include <Ice/Communicator.h>
#include <boost/lexical_cast.hpp>
#include <selectcommandUtil.impl.h>
#include <utils/dbUtils.h>
#include <git2.h>
#include <scopeExit.h>
#include <sql/maintenance/changeLogRoots.sql.h>
#include <sql/maintenance/changeLogInsert.sql.h>
#include <portage-models.h>
#include "converters.h"

namespace Gentoo {
	namespace Service {
		static
		void
		gitSafe(int func)
		{
			if (int _giterror = func < 0) {
				const git_error * e = giterr_last();
				throw GitError(_giterror, e->klass, e->message);
			}
		}

		static
		int
		onFile(const git_diff_delta * delta, float, void * fileset)
		{
			static_cast<StringList *>(fileset)->push_back(delta->old_file.path);
			static_cast<StringList *>(fileset)->push_back(delta->new_file.path);
			return 0;
		}

		static
		int
		onBinaryFile(const git_diff_delta * delta, const git_diff_binary *, void * fileset)
		{
			static_cast<StringList *>(fileset)->push_back(delta->old_file.path);
			static_cast<StringList *>(fileset)->push_back(delta->new_file.path);
			return 0;
		}

		template<typename R, typename ... P, typename ... A>
		std::unique_ptr<R, void(*)(R*)>
		gitSafeGet(int(*get)(R**, P...), void(*release)(R*), A ... p)
		{
			R * r = nullptr;
			gitSafe(get(&r, p...));
			return std::unique_ptr<R, void(*)(R*)>(r, release);
		}

		void
		Maintenance::refreshChangeLogs(const Ice::Current & c)
		{
			git_libgit2_init();
			AdHoc::ScopeExit shutdownlibgit2(&git_libgit2_shutdown);

			auto dbc = db->get();
			DB::TransactionScope tx(dbc.get());
			auto cli = dbc->modify(sql::maintenance::changeLogInsert.getSql());

			dbc->select(sql::maintenance::changeLogRoots.getSql())->forEachRow<int64_t, std::string, std::string, boost::optional<std::string>>([&cli,&c](auto repoId, const auto & repoName, const auto & repoRoot, const auto & lastCommitId) {
					cli->bindParamI(0, repoId);
					// Open repository
					auto repo = gitSafeGet(git_repository_open_ext, git_repository_free, repoRoot.c_str(), 0, nullptr);
					// Set up walker
					auto walker = gitSafeGet(git_revwalk_new, git_revwalk_free, repo.get());
					auto startref = c.adapter->getCommunicator()->getProperties()
							->getProperty("GentooBrowseAPI.ChangeLogStart." + repoName);
					if (startref.empty()) {
						gitSafe(git_revwalk_push_head(walker.get()));
					}
					else {
						git_oid oid;
						gitSafe(git_oid_fromstr(&oid, startref.c_str()));
						gitSafe(git_revwalk_push(walker.get(), &oid));
					}
					git_revwalk_sorting(walker.get(), GIT_SORT_TIME);

					git_oid oid;
					char str[GIT_OID_HEXSZ + 1];
					// Walk through revisions
					for (; !git_revwalk_next(&oid, walker.get()); ) {
						git_oid_tostr(str, sizeof(str), &oid);
						if (lastCommitId && *lastCommitId == str) {
							break;
						}
						// Get commit
						auto commit = gitSafeGet(git_commit_lookup, git_commit_free, repo.get(), &oid);

						// Get commit's tree
						auto currentTree = gitSafeGet(git_commit_tree, git_tree_free, commit.get());

						// Collect all files change in commit from all parents
						std::unique_ptr<git_tree, void(*)(git_tree*)> parentTree(nullptr, git_tree_free);
						if (git_commit_parentcount(commit.get()) > 0) {
							auto parentCommit = gitSafeGet(git_commit_parent, git_commit_free, commit.get(), 0);
							// Get parent tree
							parentTree = gitSafeGet(git_commit_tree, git_tree_free, parentCommit.get());
						}
						// Get tree to tree diff
						auto diff = gitSafeGet(git_diff_tree_to_tree, git_diff_free, repo.get(), currentTree.get(), parentTree.get(), nullptr);
						// Compare trees
						StringList fs;
						git_diff_foreach(diff.get(), onFile, onBinaryFile, nullptr, nullptr, &fs);
						// Remove duplicate mentions of files
						std::sort(fs.begin(), fs.end());
						fs.erase(std::unique(fs.begin(), fs.end()), fs.end());

						// Insert commit into DB
						cli->bindParamS(1, str);
						auto sig = git_commit_author(commit.get());
						cli->bindParamT(2, boost::posix_time::from_time_t(sig->when.time));
						cli->bindParamS(3, git_commit_summary(commit.get()));
						Utils::Database::bindOptionalS(cli.get(), 4, git_commit_body(commit.get()));
						cli->bindParamS(5, sig->name);
						cli->bindParamS(6, sig->email);
						cli->bindParamS(7, Slicer::packPqTextArray(fs));
						cli->execute();
					}
				});
		}
	}
}