summaryrefslogtreecommitdiff
path: root/gentoobrowse-api/service/maintenanceChangeLogs.cpp
blob: d0e359aa42eba70ef90d97c8bf98fd91c84717d3 (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
127
128
129
130
131
132
133
134
135
#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 <sql/maintenance/changeLogRepoCommits.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>([&cli,&c,&dbc](auto repoId, const auto & repoName, const auto & repoRoot) {
					std::set<std::string> processedChanges;
					{
						auto changes = dbc->select(sql::maintenance::changeLogRepoCommits.getSql());
						changes->bindParamI(0, repoId);
						changes->forEachRow<std::string>([&processedChanges](const auto & c) { processedChanges.insert(c); });
					}
					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);
						auto i = processedChanges.find(str);
						if (i != processedChanges.end()) {
							processedChanges.erase(i);
							continue;
						}
						// 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()));
						cli->bindParamS(4, git_commit_body(commit.get()));
						cli->bindParamS(5, sig->name);
						cli->bindParamS(6, sig->email);
						cli->bindParamS(7, Slicer::packPqTextArray(fs));
						cli->execute();
					}
				});
		}
	}
}