Compare commits

..

No commits in common. "main" and "main" have entirely different histories.
main ... main

35 changed files with 503 additions and 41634 deletions

View File

@ -1,13 +1,7 @@
all:
isort *.py lib/*py tests/*py
autoflake --in-place --remove-unused-variables *.py lib/*py tests/*py
black *.py lib/*py tests/*py
isort -rc .
autoflake -r --in-place --remove-unused-variables .
black .
test:
python3 -m unittest -v tests/*.py
update-packages:
f=$$(mktemp) ;\
osc api /source/openSUSE:Factory?view=info | grep -v lsrcmd5 | grep srcmd5= | sed -e 's,.*package=",,; s,".*,,' | grep -v : > $$f ;\
echo _project >> $$f ;\
mv $$f packages

View File

@ -1,4 +1,4 @@
sudo zypper in python3-psycopg
sudo zypper in python3-psycopg2
sudo su - postgres
# `createdb -O <LOCAL_USER> imported_git`

View File

@ -42,36 +42,16 @@ PROJECTS = [
]
def export_package(project, package, repodir, cachedir, gc):
exporter = GitExporter(URL_OBS, project, package, repodir, cachedir)
exporter.set_gc_interval(gc)
exporter.export_as_git()
def main():
parser = argparse.ArgumentParser(description="OBS history importer into git")
parser.add_argument("packages", help="OBS package names", nargs="*")
parser.add_argument(
"-p",
"--project",
default="openSUSE:Factory",
help="Project to import/export, default is openSUSE:Factory",
)
parser.add_argument("package", help="OBS package name")
parser.add_argument(
"-r",
"--repodir",
required=False,
default=pathlib.Path("repos"),
type=pathlib.Path,
help="Local git repository directory",
)
parser.add_argument(
"-c",
"--cachedir",
required=False,
type=pathlib.Path,
help="Local cache directory",
)
parser.add_argument(
"-g",
"--gc",
@ -107,22 +87,17 @@ def main():
requests_log.propagate = True
if args.export:
if len(args.packages) != 1:
print("Can only export one package")
sys.exit(1)
TestExporter(args.packages[0]).run()
TestExporter(args.package).run()
return
if not args.cachedir:
args.cachedir = pathlib.Path("~/.cache/git-import/").expanduser()
if not args.repodir:
args.repodir = pathlib.Path("repos/" + args.package)
importer = Importer(URL_OBS, args.project, args.packages)
importer = Importer(URL_OBS, "openSUSE:Factory", args.package)
importer.import_into_db()
for package in args.packages:
if not importer.package_with_scmsync(package):
export_package(args.project, package, args.repodir, args.cachedir, args.gc)
else:
logging.debug(f"{args.project}/{package} has scmsync links - skipping export")
exporter = GitExporter(URL_OBS, "openSUSE:Factory", args.package, args.repodir)
exporter.set_gc_interval(args.gc)
exporter.export_as_git()
if __name__ == "__main__":

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,6 @@
from abc import ABC, abstractmethod
class AbstractWalker(ABC):
class AbstractWalker:
"""Just a duck type, most likely not needed by python, but I
find interface classes preferable (java school)"""
@abstractmethod
def call(self, node, is_source):
pass

View File

@ -25,28 +25,18 @@ BINARY = {
".zst",
}
TEXT_MIMETYPES = {
"message/rfc822",
"application/pgp-keys",
"application/x-gnupg-keyring",
}
def is_text_mimetype(mimetype):
if mimetype.startswith("text/"):
return True
return mimetype.split(";")[0] in TEXT_MIMETYPES
def is_binary_or_large(filename, size):
"""Decide if is a binary file based on the extension or size"""
binary_suffix = BINARY
non_binary_suffix = {
".1",
".8",
".SUSE",
".asc",
".c",
".cabal",
".cfg",
".changes",
".conf",
".desktop",

View File

@ -14,6 +14,8 @@ def config(filename="database.ini", section="production"):
for param in params:
db[param[0]] = param[1]
else:
raise Exception(f"Section {section} not found in the {filename} file")
raise Exception(
"Section {0} not found in the {1} file".format(section, filename)
)
return db

View File

@ -1,6 +1,7 @@
import logging
import psycopg
import psycopg2
from psycopg2.extras import LoggingConnection
from lib.config import config
@ -16,20 +17,22 @@ class DB:
# read the connection parameters
params = config(section=self.config_section)
# connect to the PostgreSQL server
self.conn = psycopg.connect(conninfo=f"dbname={params['database']}")
logging.getLogger("psycopg.pool").setLevel(logging.INFO)
self.conn = psycopg2.connect(connection_factory=LoggingConnection, **params)
logger = logging.getLogger(__name__)
self.conn.initialize(logger)
except (Exception, psycopg.DatabaseError) as error:
except (Exception, psycopg2.DatabaseError) as error:
print(error)
raise error
def schema_version(self):
# create a cursor
with self.conn.cursor() as cur:
# execute a statement
try:
cur.execute("SELECT MAX(version) from scheme")
except psycopg.errors.UndefinedTable:
except psycopg2.errors.UndefinedTable as error:
cur.close()
self.close()
self.connect()
@ -143,9 +146,9 @@ class DB:
)
schemes[10] = (
"ALTER TABLE revisions ADD COLUMN request_id INTEGER",
"""ALTER TABLE revisions
"""ALTER TABLE revisions
ADD CONSTRAINT request_id_foreign_key
FOREIGN KEY (request_id)
FOREIGN KEY (request_id)
REFERENCES requests (id)""",
"UPDATE scheme SET version=10",
)
@ -212,51 +215,6 @@ class DB:
"CREATE INDEX ON linked_revs(considered)",
"UPDATE scheme SET version=20",
)
schemes[21] = (
"ALTER TABLE revisions ADD COLUMN api_url VARCHAR(40)",
"UPDATE revisions SET api_url='https://api.opensuse.org'",
"ALTER TABLE revisions ALTER COLUMN api_url SET NOT NULL",
"UPDATE scheme SET version=21",
)
schemes[22] = (
"""DROP TABLE IF EXISTS lfs_oids""",
"""
CREATE TABLE lfs_oids (
id SERIAL PRIMARY KEY,
project VARCHAR(255) NOT NULL,
package VARCHAR(255) NOT NULL,
filename VARCHAR(255) NOT NULL,
rev VARCHAR(40) NOT NULL,
sha256 VARCHAR(70) NOT NULL,
size INTEGER NOT NULL,
mimetype VARCHAR(255) NOT NULL,
file_md5 VARCHAR(40) NOT NULL
)
""",
"CREATE UNIQUE INDEX ON lfs_oids (sha256,size)",
"CREATE INDEX ON revisions(package)",
"""DROP TABLE IF EXISTS text_files""",
"""
CREATE TABLE text_files (
id SERIAL PRIMARY KEY,
package VARCHAR(255) NOT NULL,
filename VARCHAR(255) NOT NULL
)
""",
"CREATE UNIQUE INDEX ON text_files (package,filename)",
"""DROP TABLE IF EXISTS lfs_oid_in_package""",
"""
CREATE TABLE lfs_oid_in_package (
id SERIAL PRIMARY KEY,
lfs_oid_id INTEGER NOT NULL,
package VARCHAR(255) NOT NULL,
filename VARCHAR(255) NOT NULL
)
""",
"CREATE INDEX ON text_files(package)",
"CREATE INDEX ON lfs_oid_in_package(package)",
"UPDATE scheme SET version=22",
)
schema_version = self.schema_version()
if (schema_version + 1) not in schemes:
return
@ -270,7 +228,7 @@ class DB:
cur.execute(command)
# commit the changes
self.conn.commit()
except (Exception, psycopg.DatabaseError) as error:
except (Exception, psycopg2.DatabaseError) as error:
print(error)
self.close()
raise error

View File

@ -1,15 +1,15 @@
from __future__ import annotations
import logging
from hashlib import md5
from pathlib import Path
from typing import Optional
from lib.db import DB
from lib.obs_revision import OBSRevision
from lib.request import Request
class DBRevision:
def __init__(self, db: DB, row: tuple):
def __init__(self, row):
# need to stay in sync with the schema creation in db.py
(
self.dbid,
@ -25,12 +25,9 @@ class DBRevision:
self.request_number,
self.request_id,
self.files_hash,
self.api_url,
) = row
self.rev = float(self.rev)
self._files = None
self.db = db
self.git_commit = None
def short_string(self):
return f"{self.project}/{self.package}/{self.rev}"
@ -51,29 +48,7 @@ class DBRevision:
return self.package < other.package
return self.rev < other.rev
def request_accept_message(self):
request = Request.find(self.db, self.request_id)
msg = f"Accepting request {request.number} from {request.source_project}\n\n"
msg += self.comment.strip()
url = self.api_url.replace("api.", "build.")
msg += f"\n\nOBS-URL: {url}/request/show/{self.request_number}"
return msg
def git_commit_message(self):
msg = ""
if self.request_id:
msg = self.request_accept_message()
else:
msg = self.comment.strip() + "\n"
url = self.api_url.replace("api.", "build.")
if self.rev == int(self.rev):
# do not link to fake revisions
msg += f"\nOBS-URL: {url}/package/show/{self.project}/{self.package}?expand=0&rev={int(self.rev)}"
else:
msg += f"\nOBS-URL: {url}/package/show/{self.project}/{self.package}?expand=0&rev={self.expanded_srcmd5}"
return msg
def as_dict(self):
def as_dict(self, db):
"""Return a dict we can put into YAML for test cases"""
ret = {
"project": self.project,
@ -85,27 +60,26 @@ class DBRevision:
"comment": self.comment,
"broken": self.broken,
"expanded_srcmd5": self.expanded_srcmd5,
"api_url": self.api_url,
"files_hash": self.files_hash,
"files": self.files_list(),
"files": self.files_list(db),
}
if self.request_id:
ret["request"] = Request.find(self.db, self.request_id).as_dict()
ret["request"] = Request.find(db, self.request_id).as_dict()
return ret
def links_to(self, project: str, package: str) -> None:
with self.db.cursor() as cur:
def links_to(self, db, project, package):
with db.cursor() as cur:
cur.execute(
"INSERT INTO links (revision_id, project, package) VALUES (%s,%s,%s)",
(self.dbid, project, package),
)
@staticmethod
def import_obs_rev(db: DB, revision: OBSRevision):
@classmethod
def import_obs_rev(cls, db, revision):
with db.cursor() as cur:
cur.execute(
"""INSERT INTO revisions (project, package, rev, unexpanded_srcmd5, commit_time, userid, comment, request_number, api_url)
VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s)""",
"""INSERT INTO revisions (project, package, rev, unexpanded_srcmd5, commit_time, userid, comment, request_number)
VALUES(%s, %s, %s, %s, %s, %s, %s, %s)""",
(
revision.project,
revision.package,
@ -115,17 +89,12 @@ class DBRevision:
revision.userid,
revision.comment,
revision.request_number,
revision.obs.url,
),
)
return DBRevision.fetch_revision(
db, revision.project, revision.package, revision.rev
)
return cls.fetch_revision(db, revision.project, revision.package, revision.rev)
@staticmethod
def fetch_revision(db, project, package, rev):
"""Technically we would need the api_url as well, but we assume projects are unique
(e.g. not importing SLE from obs)"""
with db.cursor() as cur:
cur.execute(
"SELECT * FROM revisions where project=%s and package=%s and rev=%s",
@ -133,21 +102,16 @@ class DBRevision:
)
row = cur.fetchone()
if row:
return DBRevision(db, row)
return DBRevision(row)
@staticmethod
def max_rev(db, project, package):
def latest_revision(db, project, package):
with db.cursor() as cur:
cur.execute(
"SELECT MAX(rev) FROM revisions where project=%s and package=%s",
(project, package),
)
return cur.fetchone()[0]
return None
@staticmethod
def latest_revision(db, project, package):
max = DBRevision.max_rev(db, project, package)
max = cur.fetchone()[0]
if max:
return DBRevision.fetch_revision(db, project, package, max)
return None
@ -161,13 +125,13 @@ class DBRevision:
)
ret = []
for row in cur.fetchall():
ret.append(DBRevision(db, row))
ret.append(DBRevision(row))
return ret
def linked_rev(self):
def linked_rev(self, db):
if self.broken:
return None
with self.db.cursor() as cur:
with db.cursor() as cur:
cur.execute(
"SELECT project,package FROM links where revision_id=%s", (self.dbid,)
)
@ -179,33 +143,26 @@ class DBRevision:
"SELECT * FROM revisions where project=%s and package=%s and commit_time <= %s ORDER BY commit_time DESC LIMIT 1",
(project, package, self.commit_time),
)
revisions = [DBRevision(self.db, row) for row in cur.fetchall()]
revisions = [DBRevision(row) for row in cur.fetchall()]
if revisions:
return revisions[0]
else:
self.set_broken()
self.set_broken(db)
return None
def set_broken(self):
with self.db.cursor() as cur:
def set_broken(self, db):
with db.cursor() as cur:
cur.execute("UPDATE revisions SET broken=TRUE where id=%s", (self.dbid,))
def import_dir_list(self, xml):
with self.db.cursor() as cur:
def import_dir_list(self, db, xml):
with db.cursor() as cur:
cur.execute(
"UPDATE revisions SET expanded_srcmd5=%s where id=%s",
(xml.get("srcmd5"), self.dbid),
)
for entry in xml.findall("entry"):
# this file creates easily 100k commits and is just useless data :(
# unfortunately it's stored in the same meta package as the project config
if (
entry.get("name") == "_staging_workflow"
and self.package == "_project"
):
continue
cur.execute(
"""INSERT INTO files (name, md5, size, mtime, revision_id)
"""INSERT INTO files (name, md5, size, mtime, revision_id)
VALUES (%s,%s,%s,%s,%s)""",
(
entry.get("name"),
@ -216,19 +173,15 @@ class DBRevision:
),
)
def previous_commit(self):
return DBRevision.fetch_revision(
self.db, self.project, self.package, int(self.rev) - 1
)
def previous_commit(self, db):
return self.fetch_revision(db, self.project, self.package, int(self.rev) - 1)
def next_commit(self):
return DBRevision.fetch_revision(
self.db, self.project, self.package, int(self.rev) + 1
)
def next_commit(self, db):
return self.fetch_revision(db, self.project, self.package, int(self.rev) + 1)
def calculate_files_hash(self):
def calculate_files_hash(self, db):
m = md5()
for file_dict in self.files_list():
for file_dict in self.files_list(db):
m.update(
(
file_dict["name"]
@ -240,10 +193,10 @@ class DBRevision:
)
return m.hexdigest()
def files_list(self):
def files_list(self, db):
if self._files:
return self._files
with self.db.cursor() as cur:
with db.cursor() as cur:
cur.execute("SELECT * from files where revision_id=%s", (self.dbid,))
self._files = []
for row in cur.fetchall():
@ -254,23 +207,27 @@ class DBRevision:
self._files.sort(key=lambda x: x["name"])
return self._files
def calc_delta(self, current_rev: DBRevision | None):
def calc_delta(self, db: DB, current_rev: Optional[DBRevision]):
"""Calculate the list of files to download and to delete.
Param current_rev is the revision that's currently checked out.
If it's None, the repository is empty.
"""
to_download = []
to_delete = []
if current_rev:
old_files = {
e["name"]: f"{e['md5']}-{e['size']}" for e in current_rev.files_list()
e["name"]: f"{e['md5']}-{e['size']}" for e in current_rev.files_list(db)
}
else:
old_files = dict()
for entry in self.files_list():
for entry in self.files_list(db):
if old_files.get(entry["name"]) != f"{entry['md5']}-{entry['size']}":
to_download.append((Path(entry["name"]), entry["size"], entry["md5"]))
logging.debug(f"Download {entry['name']}")
to_download.append((entry["name"], entry["md5"]))
old_files.pop(entry["name"], None)
to_delete = [Path(e) for e in old_files.keys()]
for entry in old_files.keys():
logging.debug(f"Delete {entry}")
to_delete.append(entry)
return to_download, to_delete
@staticmethod
@ -288,9 +245,9 @@ class DBRevision:
"""Used in test cases to read a revision from fixtures into the test database"""
with db.cursor() as cur:
cur.execute(
"""INSERT INTO revisions (project, package, rev, unexpanded_srcmd5, expanded_srcmd5,
commit_time, userid, comment, broken, files_hash, api_url)
VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id""",
"""INSERT INTO revisions (project, package, rev, unexpanded_srcmd5, expanded_srcmd5,
commit_time, userid, comment, broken, files_hash)
VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id""",
(
rev_dict["project"],
rev_dict["package"],
@ -302,7 +259,6 @@ class DBRevision:
rev_dict["comment"],
rev_dict["broken"],
rev_dict["files_hash"],
rev_dict.get("api_url", "https://api.opensuse.org"),
),
)
rev_id = cur.fetchone()[0]

View File

@ -9,8 +9,12 @@ class FlatNode:
self.parent2 = parent2
def __str__(self) -> str:
p1_str = f" p1:{self.parent1.short_string()}" if self.parent1 else ""
p2_str = f" p2:{self.parent2.short_string()}" if self.parent2 else ""
p1_str = ""
if self.parent1:
p1_str = f" p1:{self.parent1.short_string()}"
p2_str = ""
if self.parent2:
p2_str = f" p2:{self.parent2.short_string()}"
return f"{self.branch} c:{self.commit.short_string()}{p1_str}{p2_str}"
@ -32,7 +36,8 @@ class FlatTreeWalker(AbstractWalker):
def handle_source_node(self, node) -> None:
if self.rebase_devel and node.parent and node.parent.merged_into:
self.add("devel", node.revision, node.parent.merged_into.revision)
elif node.parent:
return
if node.parent:
self.add("devel", node.revision, node.parent.revision)
elif self.last_merge:
self.add("devel", node.revision, self.last_merge.parent.revision)

View File

@ -1,10 +1,9 @@
import fnmatch
import logging
import os
import pathlib
import subprocess
import requests
import pygit2
from lib.binary import BINARY
@ -19,6 +18,12 @@ class Git:
self.committer = committer
self.committer_email = committer_email
self.repo = None
def is_open(self):
return self.repo is not None
# TODO: Extend it to packages and files
def exists(self):
"""Check if the path is a valid git repository"""
return (self.path / ".git").exists()
@ -26,72 +31,36 @@ class Git:
def create(self):
"""Create a local git repository"""
self.path.mkdir(parents=True, exist_ok=True)
self.open()
def git_run(self, args, **kwargs):
"""Run a git command"""
if "env" in kwargs:
envs = kwargs["env"].copy()
del kwargs["env"]
else:
envs = os.environ.copy()
envs["GIT_LFS_SKIP_SMUDGE"] = "1"
envs["GIT_CONFIG_GLOBAL"] = "/dev/null"
return subprocess.run(
["git"] + args,
cwd=self.path,
check=True,
env=envs,
**kwargs,
)
def open(self):
if not self.exists():
self.git_run(["init", "--object-format=sha256", "-b", "factory"])
self.git_run(["config", "lfs.allowincompletepush", "true"])
# Convert the path to string, to avoid some limitations in
# older pygit2
self.repo = pygit2.init_repository(str(self.path))
return self
def is_dirty(self):
"""Check if there is something to commit"""
status_str = self.git_run(
["status", "--porcelain=2"],
stdout=subprocess.PIPE,
).stdout.decode("utf-8")
return len(list(filter(None, status_str.split("\n")))) > 0
assert self.is_open()
return self.repo.status()
def branches(self):
br = (
self.git_run(
["for-each-ref", "--format=%(refname:short)", "refs/heads/"],
stdout=subprocess.PIPE,
)
.stdout.decode("utf-8")
.split()
)
if len(br) == 0:
br.append("factory") # unborn branch?
return br
return list(self.repo.branches)
def branch(self, branch, commit="HEAD"):
commit = (
self.git_run(
["rev-parse", "--verify", "--end-of-options", commit + "^{commit}"],
stdout=subprocess.PIPE,
)
.stdout.decode("utf-8")
.strip()
)
return self.git_run(["branch", branch, commit])
def branch(self, branch, commit=None):
if not commit:
commit = self.repo.head
else:
commit = self.repo.get(commit)
self.repo.branches.local.create(branch, commit)
def checkout(self, branch):
"""Checkout into the branch HEAD"""
new_branch = False
ref = f"refs/heads/{branch}"
if branch not in self.branches():
self.git_run(["switch", "-q", "--orphan", branch])
self.repo.references["HEAD"].set_target(ref)
new_branch = True
else:
ref = f"refs/heads/{branch}"
if (self.path / ".git" / ref).exists():
self.git_run(["switch", "--no-guess", "-q", branch])
self.repo.checkout(ref)
return new_branch
def commit(
@ -104,8 +73,10 @@ class Git:
committer=None,
committer_email=None,
committer_time=None,
allow_empty=False,
):
"""Add all the files and create a new commit in the current HEAD"""
assert allow_empty or self.is_dirty()
if not committer:
committer = self.committer if self.committer else self.user
@ -114,80 +85,125 @@ class Git:
)
committer_time = committer_time if committer_time else user_time
if self.is_dirty():
self.git_run(["add", "--all", "."])
try:
self.repo.index.add_all()
except pygit2.GitError as e:
if not allow_empty:
raise e
tree_id = (
self.git_run(["write-tree"], stdout=subprocess.PIPE)
.stdout.decode("utf-8")
.strip()
self.repo.index.write()
author = pygit2.Signature(user, user_email, int(user_time.timestamp()))
committer = pygit2.Signature(
committer, committer_email, int(committer_time.timestamp())
)
if not parents:
try:
parents = [self.repo.head.target]
except pygit2.GitError as e:
parents = []
if not allow_empty:
raise e
tree = self.repo.index.write_tree()
return self.repo.create_commit(
"HEAD", author, committer, message, tree, parents
)
parent_array = []
if isinstance(parents, list):
for parent in filter(None, parents):
parent_array = parent_array + ["-p", parent]
elif isinstance(parents, str):
parent_array = ["-p", parents]
def merge(
self,
user,
user_email,
user_time,
message,
commit,
committer=None,
committer_email=None,
committer_time=None,
clean_on_conflict=True,
merged=False,
allow_empty=False,
):
new_branch = False
commit_id = (
self.git_run(
["commit-tree"] + parent_array + [tree_id],
env={
"GIT_AUTHOR_NAME": user,
"GIT_AUTHOR_EMAIL": user_email,
"GIT_AUTHOR_DATE": f"{int(user_time.timestamp())} +0000",
"GIT_COMMITTER_NAME": committer,
"GIT_COMMITTER_EMAIL": committer_email,
"GIT_COMMITTER_DATE": f"{int(committer_time.timestamp())} +0000",
},
input=message.encode("utf-8"),
stdout=subprocess.PIPE,
)
.stdout.decode("utf-8")
.rstrip()
)
self.git_run(["reset", "--soft", commit_id])
return commit_id
if not merged:
try:
self.repo.merge(commit)
except KeyError:
# If it is the first commit, we will have a missing
# "HEAD", but the files will be there. We can proceed
# to the commit directly.
new_branch = True
def branch_head(self, branch="HEAD"):
return (
self.git_run(
["rev-parse", "--verify", "--end-of-options", branch],
stdout=subprocess.PIPE,
)
.stdout.decode("utf-8")
.strip()
if not merged and self.repo.index.conflicts:
for conflict in self.repo.index.conflicts:
conflict = [c for c in conflict if c]
if conflict:
logging.info(f"CONFLICT {conflict[0].path}")
if clean_on_conflict:
self.clean()
# Now I miss Rust enums
return "CONFLICT"
# Some merges are empty in OBS (no changes, not sure
# why), for now we signal them
if not allow_empty and not self.is_dirty():
# I really really do miss Rust enums
return "EMPTY"
if new_branch:
parents = [commit]
else:
parents = [
self.repo.head.target,
commit,
]
commit = self.commit(
user,
user_email,
user_time,
message,
parents,
committer,
committer_email,
committer_time,
allow_empty=allow_empty,
)
def set_branch_head(self, branch, commit):
return self.git_run(["update-ref", f"refs/heads/{branch}", commit])
return commit
def merge_abort(self):
self.repo.state_cleanup()
def last_commit(self):
try:
return self.repo.head.target
except:
return None
def branch_head(self, branch):
return self.repo.references["refs/heads/" + branch].target
def gc(self):
logging.debug(f"Garbage recollect and repackage {self.path}")
self.git_run(
["gc", "--auto"],
logging.info(f"Garbage recollect and repackage {self.path}")
subprocess.run(
["git", "gc", "--auto"],
cwd=self.path,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
# def clean(self):
# for path, _ in self.repo.status().items():
# logging.debug(f"Cleaning {path}")
# try:
# (self.path / path).unlink()
# self.repo.index.remove(path)
# except Exception as e:
# logging.warning(f"Error removing file {path}: {e}")
def clean(self):
for path, _ in self.repo.status().items():
logging.debug(f"Cleaning {path}")
try:
(self.path / path).unlink()
self.repo.index.remove(path)
except Exception as e:
logging.warning(f"Error removing file {path}: {e}")
def add(self, filename):
self.git_run(["add", ":(literal)" + str(filename)])
def add_default_gitignore(self):
if not (self.path / ".gitignore").exists():
with (self.path / ".gitignore").open("w") as f:
f.write(".osc\n")
self.add(".gitignore")
self.repo.index.add(filename)
def add_default_lfs_gitattributes(self, force=False):
if not (self.path / ".gitattributes").exists() or force:
@ -240,49 +256,11 @@ class Git:
)
return any(fnmatch.fnmatch(filename, line) for line in patterns)
def remove(self, file: pathlib.Path):
self.git_run(
["rm", "-q", "-f", "--ignore-unmatch", ":(literal)" + file.name],
)
def remove(self, filename):
self.repo.index.remove(filename)
(self.path / filename).unlink()
patterns = self.get_specific_lfs_gitattributes()
if file.name in patterns:
patterns.remove(file.name)
if filename in patterns:
patterns.remove(filename)
self.add_specific_lfs_gitattributes(patterns)
def add_gitea_remote(self, package):
repo_name = package.replace("+", "_")
org_name = "rpm"
if not os.getenv("GITEA_TOKEN"):
logging.warning("Not adding a remote due to missing $GITEA_TOKEN")
return
url = f"https://src.opensuse.org/api/v1/org/{org_name}/repos"
response = requests.post(
url,
data={"name": repo_name, "object_format_name": "sha256"},
headers={"Authorization": f"token {os.getenv('GITEA_TOKEN')}"},
timeout=10,
)
# 409 Conflict (Already existing)
# 201 Created
if response.status_code not in (201, 409):
print(response.data)
url = f"gitea@src.opensuse.org:{org_name}/{repo_name}.git"
self.git_run(
["remote", "add", "origin", url],
)
def push(self, force=False):
if "origin" not in self.git_run(
["remote"],
stdout=subprocess.PIPE,
).stdout.decode("utf-8"):
logging.warning("Not pushing to remote because no 'origin' configured")
return
cmd = ["push"]
if force:
cmd.append("-f")
cmd += ["origin", "--all"]
self.git_run(cmd)

View File

@ -6,48 +6,99 @@ import yaml
from lib.binary import is_binary_or_large
from lib.db import DB
from lib.git import Git
from lib.lfs_oid import LFSOid
from lib.obs import OBS
from lib.proxy_sha256 import ProxySHA256
from lib.proxy_sha256 import ProxySHA256, md5
from lib.tree_builder import TreeBuilder
from lib.user import User
class GitExporter:
def __init__(self, api_url, project, package, repodir, cachedir):
self.obs = OBS(api_url)
def __init__(self, api_url, project, package, repodir):
self.obs = OBS()
self.project = project
self.package = package
self.db = DB()
self.proxy_sha256 = ProxySHA256(self.obs, self.db)
# TODO: Store the api url in the revision
self.obs.change_url(api_url)
self.proxy_sha256 = ProxySHA256(self.obs, enabled=True)
self.git = Git(
repodir / package,
repodir,
committer="Git OBS Bridge",
committer_email="obsbridge@suse.de",
)
if self.git.exists():
self.git.open()
else:
self.git.create()
self.git.add_gitea_remote(package)
).create()
self.state_file = os.path.join(self.git.path, ".git", "_flat_state.yaml")
self.gc_interval = 200
self.cachedir = cachedir
def download(self, revision):
obs_files = self.obs.files(revision.project, revision.package, revision.srcmd5)
git_files = {
(f.name, f.stat().st_size, md5(f))
for f in self.git.path.iterdir()
if f.is_file() and f.name not in (".gitattributes")
}
# Overwrite ".gitattributes" with the
self.git.add_default_lfs_gitattributes(force=True)
# Download each file in OBS if it is not a binary (or large)
# file
for (name, size, file_md5) in obs_files:
# this file creates easily 100k commits and is just useless data :(
# unfortunately it's stored in the same meta package as the project config
if revision.package == "_project" and name == "_staging_workflow":
continue
# have such files been detected as text mimetype before?
is_text = self.proxy_sha256.is_text(name)
if not is_text and is_binary_or_large(name, size):
file_sha256 = self.proxy_sha256.get_or_put(
revision.project,
revision.package,
name,
revision.srcmd5,
file_md5,
size,
)
self.git.add_lfs(name, file_sha256["sha256"], size)
else:
if (name, size, file_md5) not in git_files:
logging.debug(f"Download {name}")
self.obs.download(
revision.project,
revision.package,
name,
revision.srcmd5,
self.git.path,
file_md5=file_md5,
)
# Validate the MD5 of the downloaded file
if md5(self.git.path / name) != file_md5:
raise Exception(f"Download error in {name}")
self.git.add(name)
# Remove extra files
obs_names = {n for (n, _, _) in obs_files}
git_names = {n for (n, _, _) in git_files}
for name in git_names - obs_names:
logging.debug(f"Remove {name}")
self.git.remove(name)
def set_gc_interval(self, gc):
self.gc_interval = gc
def check_repo_state(self, flats, branch_state):
def export_as_git(self):
db = DB()
tree = TreeBuilder(db).build(self.project, self.package)
flats = tree.as_flat_list()
branch_state = {"factory": None, "devel": None}
state_data = dict()
if os.path.exists(self.state_file):
with open(self.state_file) as f:
with open(self.state_file, "r") as f:
state_data = yaml.safe_load(f)
if not isinstance(state_data, dict):
if type(state_data) != dict:
state_data = {}
left_to_commit = []
for flat in reversed(flats):
found_state = False
for branch in ["factory"]:
for branch in ["factory", "devel"]:
if flat.commit.dbid == state_data.get(branch):
branch_state[branch] = flat.commit
flat.commit.git_commit = self.git.branch_head(branch)
@ -58,116 +109,55 @@ class GitExporter:
found_state = True
if not found_state:
left_to_commit.append(flat)
return left_to_commit
def export_as_git(self):
if os.getenv("CHECK_ALL_LFS"):
LFSOid.check_all(self.db, self.package)
tree = TreeBuilder(self.db).build(self.project, self.package)
flats = tree.as_flat_list()
branch_state = {"factory": None, "devel": None}
left_to_commit = self.check_repo_state(flats, branch_state)
if not left_to_commit:
return
logging.info(f"Commiting into {self.git.path}")
self.run_gc()
users = dict()
gc_cnt = self.gc_interval
if len(left_to_commit) > 0:
self.git.gc()
for flat in left_to_commit:
if flat.commit.userid not in users:
users[flat.commit.userid] = User.find(self.db, flat.commit.userid)
flat.user = users[flat.commit.userid]
self.gc_cnt -= 1
if self.gc_cnt <= 0 and self.gc_interval:
self.run_gc()
gc_cnt -= 1
if gc_cnt <= 0 and self.gc_interval:
self.git.gc()
gc_cnt = self.gc_interval
logging.debug(f"Committing {flat}")
self.commit_flat(flat, branch_state)
self.commit_flat(db, flat, branch_state)
# make sure that we create devel branch
if not branch_state["devel"]:
logging.debug("force creating devel")
self.git.set_branch_head("devel", self.git.branch_head("factory"))
self.git.push(force=True)
def run_gc(self):
self.gc_cnt = self.gc_interval
self.git.gc()
def is_lfs_file(self, package, filename, size):
if not is_binary_or_large(filename, size):
return False
return not self.proxy_sha256.is_text(package, filename)
def commit_file(self, flat, file, size, md5):
# have such files been detected as text mimetype before?
if self.is_lfs_file(flat.commit.package, file.name, size):
file_sha256 = self.proxy_sha256.get_or_put(
flat.commit.project,
flat.commit.package,
file.name,
flat.commit.expanded_srcmd5,
md5,
size,
)
# as it's newly registered, it might be a text file now, so double check
if not self.proxy_sha256.is_text(flat.commit.package, file.name):
self.git.add_lfs(file.name, file_sha256, size)
return
self.commit_non_lfs_file(flat, file, md5)
def commit_non_lfs_file(self, flat, file, md5):
self.obs.change_url(flat.commit.api_url)
self.obs.download(
flat.commit.project,
flat.commit.package,
file.name,
flat.commit.expanded_srcmd5,
self.git.path,
self.cachedir,
file_md5=md5,
)
self.git.add(file)
def branch_fits_parent1(self, flat, branch_state):
if branch_state[flat.branch] is None:
# everything fits nothing
def limit_download(self, file):
if file.endswith(".spec") or file.endswith(".changes"):
return True
return flat.parent1 == branch_state[flat.branch]
return False
def commit_flat(self, flat, branch_state):
def commit_flat(self, db, flat, branch_state):
parents = []
self.git.checkout(flat.branch)
if flat.parent1:
if not self.branch_fits_parent1(flat, branch_state):
logging.debug(f"Reset {flat.branch} onto {flat.parent1.short_string()}")
assert flat.parent1.git_commit
self.git.set_branch_head(flat.branch, flat.parent1.git_commit)
self.git.checkout(flat.branch)
parents.append(flat.parent1.git_commit)
if flat.parent2:
assert flat.parent2.git_commit
parents.append(flat.parent2.git_commit)
# create file if not existant
self.git.add_default_lfs_gitattributes(force=False)
self.git.add_default_gitignore()
to_download, to_delete = flat.commit.calc_delta(branch_state[flat.branch])
if flat.parent2:
parents.append(flat.parent2.git_commit)
to_download, to_delete = flat.commit.calc_delta(db, branch_state[flat.branch])
for file in to_delete:
if not self.limit_download(file):
continue
self.git.remove(file)
for file, size, md5 in to_download:
self.commit_file(flat, file, size, md5)
for file, md5 in to_download:
if not self.limit_download(file):
continue
self.obs.download(
flat.commit.project,
flat.commit.package,
file,
flat.commit.expanded_srcmd5,
self.git.path,
file_md5=md5,
)
self.git.add(file)
commit = self.git.commit(
flat.user.realname,
flat.user.email,
f"OBS User {flat.commit.userid}",
"null@suse.de",
flat.commit.commit_time,
flat.commit.git_commit_message(),
# TODO: Normalize better the commit message
f"{flat.commit.comment}\n\n{flat.commit}",
allow_empty=True,
parents=parents,
)
flat.commit.git_commit = commit

View File

@ -1,20 +0,0 @@
import functools
import hashlib
def _hash(hash_alg, file_or_path):
h = hash_alg()
def __hash(f):
while chunk := f.read(1024 * 4):
h.update(chunk)
if hasattr(file_or_path, "read"):
__hash(file_or_path)
else:
with file_or_path.open("rb") as f:
__hash(f)
return h.hexdigest()
md5 = functools.partial(_hash, hashlib.md5)

View File

@ -1,5 +1,4 @@
import logging
import pathlib
import xml.etree.ElementTree as ET
from lib.db import DB
@ -9,65 +8,46 @@ from lib.obs_revision import OBSRevision
from lib.user import User
def refresh_package(importer, project, package):
importer.refresh_package(project, package)
def import_request(importer, number):
importer.import_request(number)
def import_rev(importer, rev):
importer.import_rev(rev)
class Importer:
def __init__(self, api_url, project, packages):
# Import multiple Factory packages into the database
self.packages = packages
def __init__(self, api_url, project, package):
# Import a Factory package into the database
self.package = package
self.project = project
self.scmsync_cache = dict()
self.packages_with_scmsync = set()
self.db = DB()
self.obs = OBS(api_url)
assert not self.has_scmsync(project)
self.obs = OBS()
assert project == "openSUSE:Factory"
self.obs.change_url(api_url)
self.refreshed_packages = set()
self.gone_packages_set = None
def import_request(self, number):
self.obs.request(number).import_into_db(self.db)
def update_db_package(self, project, package):
def update_db_package(self, db, project, package):
root = self.obs._history(project, package)
if root is None:
return
latest = DBRevision.max_rev(self.db, project, package)
latest = DBRevision.latest_revision(db, project, package)
for r in root.findall("revision"):
rev = OBSRevision(self.obs, project, package).parse(r)
if not latest or rev.rev > latest:
dbrev = DBRevision.import_obs_rev(self.db, rev)
if not latest or rev.rev > latest.rev:
dbrev = DBRevision.import_obs_rev(db, rev)
try:
root = rev.read_link()
except ET.ParseError:
dbrev.set_broken()
dbrev.set_broken(db)
continue
if root is not None:
tprj = root.get("project") or project
tpkg = root.get("package") or package
dbrev.links_to(tprj, tpkg)
dbrev.links_to(db, tprj, tpkg)
def find_linked_revs(self):
with self.db.cursor() as cur:
def find_linked_revs(self, db):
with db.cursor() as cur:
cur.execute(
"""SELECT * from revisions WHERE id in (SELECT l.revision_id FROM links l
LEFT JOIN linked_revs lrevs ON lrevs.revision_id=l.revision_id
WHERE lrevs.id IS NULL) and broken is FALSE;"""
)
for row in cur.fetchall():
rev = DBRevision(self.db, row)
linked_rev = rev.linked_rev()
rev = DBRevision(row)
linked_rev = rev.linked_rev(db)
if not linked_rev:
logging.debug(f"No link {rev}")
continue
@ -77,8 +57,8 @@ class Importer:
(rev.dbid, linked_rev.dbid),
)
def fetch_all_linked_packages(self, project, package):
with self.db.cursor() as cur:
def fetch_all_linked_packages(self, db, project, package):
with db.cursor() as cur:
cur.execute(
"""SELECT DISTINCT l.project, l.package from links l JOIN revisions r
on r.id=l.revision_id WHERE r.project=%s AND r.package=%s""",
@ -87,36 +67,36 @@ class Importer:
for row in cur.fetchall():
(lproject, lpackage) = row
# recurse
self.refresh_package(lproject, lpackage)
self.refresh_package(db, lproject, lpackage)
def find_fake_revisions(self):
with self.db.cursor() as cur:
def find_fake_revisions(self, db):
with db.cursor() as cur:
cur.execute(
"SELECT * from revisions WHERE id in (SELECT linked_id from linked_revs WHERE considered=FALSE)"
)
for row in cur.fetchall():
self._find_fake_revision(DBRevision(self.db, row))
self._find_fake_revision(db, DBRevision(row))
def _find_fake_revision(self, rev):
prev = rev.previous_commit()
def _find_fake_revision(self, db, rev):
prev = rev.previous_commit(db)
if not prev:
with self.db.cursor() as cur:
with db.cursor() as cur:
cur.execute(
"UPDATE linked_revs SET considered=TRUE where linked_id=%s",
(rev.dbid,),
)
return
with self.db.cursor() as cur:
with db.cursor() as cur:
cur.execute(
"""SELECT * FROM revisions WHERE id IN
(SELECT revision_id from linked_revs WHERE linked_id=%s)
(SELECT revision_id from linked_revs WHERE linked_id=%s)
AND commit_time <= %s ORDER BY commit_time""",
(prev.dbid, rev.commit_time),
)
last_linked = None
for linked in cur.fetchall():
linked = DBRevision(self.db, linked)
nextrev = linked.next_commit()
linked = DBRevision(linked)
nextrev = linked.next_commit(db)
if nextrev and nextrev.commit_time < rev.commit_time:
continue
last_linked = linked
@ -127,7 +107,7 @@ class Importer:
if not last_linked:
return
with self.db.cursor() as cur:
with db.cursor() as cur:
linked = last_linked
cur.execute(
"SELECT 1 FROM fake_revs where revision_id=%s AND linked_id=%s",
@ -140,10 +120,10 @@ class Importer:
)
return
fake_rev = linked.rev + rev.rev / 1000.0
comment = f"Updating link to change in {rev.project}/{rev.package} revision {int(rev.rev)}"
comment = f"Updating link to change in {rev.project}/{rev.package} revision {rev.rev}"
cur.execute(
"""INSERT INTO revisions (project,package,rev,unexpanded_srcmd5,
commit_time, userid, comment, api_url) VALUES(%s,%s,%s,%s,%s,%s,%s,%s) RETURNING id""",
"""INSERT INTO revisions (project,package,rev,unexpanded_srcmd5,
commit_time, userid, comment) VALUES(%s,%s,%s,%s,%s,%s,%s) RETURNING id""",
(
linked.project,
linked.package,
@ -152,7 +132,6 @@ class Importer:
rev.commit_time,
"buildservice-autocommit",
comment,
linked.api_url,
),
)
new_id = cur.fetchone()[0]
@ -165,115 +144,70 @@ class Importer:
(rev.dbid, linked.dbid),
)
def revisions_without_files(self, package):
logging.debug(f"revisions_without_files({package})")
with self.db.cursor() as cur:
def revisions_without_files(self, db):
with db.cursor() as cur:
cur.execute(
"SELECT * FROM revisions WHERE package=%s AND broken=FALSE AND expanded_srcmd5 IS NULL",
(package,),
"SELECT * FROM revisions WHERE broken=FALSE AND expanded_srcmd5 IS NULL"
)
return [DBRevision(self.db, row) for row in cur.fetchall()]
return [DBRevision(row) for row in cur.fetchall()]
def import_rev(self, rev):
with self.db.cursor() as cur:
cur.execute(
"""SELECT unexpanded_srcmd5 from revisions WHERE
id=(SELECT linked_id FROM linked_revs WHERE revision_id=%s)""",
(rev.dbid,),
)
linked_rev = cur.fetchone()
if linked_rev:
linked_rev = linked_rev[0]
obs_dir_list = self.obs.list(
rev.project, rev.package, rev.unexpanded_srcmd5, linked_rev
)
if obs_dir_list:
rev.import_dir_list(obs_dir_list)
md5 = rev.calculate_files_hash()
with self.db.cursor() as cur:
def fill_file_lists(self, db):
self.find_linked_revs(db)
self.find_fake_revisions(db)
for rev in self.revisions_without_files(db):
with db.cursor() as cur:
cur.execute(
"UPDATE revisions SET files_hash=%s WHERE id=%s",
(md5, rev.dbid),
"""SELECT unexpanded_srcmd5 from revisions WHERE
id=(SELECT linked_id FROM linked_revs WHERE revision_id=%s)""",
(rev.dbid,),
)
else:
rev.set_broken()
linked_rev = cur.fetchone()
if linked_rev:
linked_rev = linked_rev[0]
list = self.obs.list(
rev.project, rev.package, rev.unexpanded_srcmd5, linked_rev
)
if list:
rev.import_dir_list(db, list)
md5 = rev.calculate_files_hash(db)
with db.cursor() as cur:
cur.execute(
"UPDATE revisions SET files_hash=%s WHERE id=%s",
(md5, rev.dbid),
)
else:
rev.set_broken(db)
def fill_file_lists(self):
self.find_linked_revs()
self.find_fake_revisions()
for package in self.packages:
for rev in self.revisions_without_files(package):
print(f"rev {rev} is without files")
self.import_rev(rev)
def refresh_package(self, project, package):
def refresh_package(self, db, project, package):
key = f"{project}/{package}"
if key in self.refreshed_packages:
# refreshing once is good enough
return
if self.package_gone(key):
return
logging.debug(f"Refresh {project}/{package}")
self.refreshed_packages.add(key)
if self.has_scmsync(project) or self.has_scmsync(key):
self.packages_with_scmsync.add(package)
logging.debug(f"{project}/{package} already in Git - skipping")
return
self.update_db_package(project, package)
self.fetch_all_linked_packages(project, package)
self.update_db_package(db, project, package)
self.fetch_all_linked_packages(db, project, package)
def import_into_db(self):
for package in self.packages:
refresh_package(self, self.project, package)
db = DB()
self.db.conn.commit()
for number in DBRevision.requests_to_fetch(self.db):
self.import_request(number)
self.db.conn.commit()
with self.db.cursor() as cur:
self.refresh_package(db, self.project, self.package)
for number in DBRevision.requests_to_fetch(db):
self.obs.request(number).import_into_db(db)
with db.cursor() as cur:
cur.execute(
"""SELECT DISTINCT source_project,source_package FROM requests
WHERE id IN (SELECT request_id FROM revisions WHERE project=%s and package = ANY(%s));""",
(self.project, self.packages),
"""SELECT DISTINCT source_project,source_package FROM requests
WHERE id IN (SELECT request_id FROM revisions WHERE project=%s and package=%s);""",
(self.project, self.package),
)
for project, package in cur.fetchall():
self.refresh_package(project, package)
self.refresh_package(db, project, package)
self.db.conn.commit()
missing_users = User.missing_users(self.db)
missing_users = User.missing_users(db)
for userid in missing_users:
missing_user = self.obs.user(userid)
if missing_user:
missing_user.import_into_db(self.db)
self.db.conn.commit()
self.fill_file_lists()
self.db.conn.commit()
def package_gone(self, key):
if not self.gone_packages_set:
self.gone_packages_set = set()
with open(pathlib.Path(__file__).parent.parent / "gone-packages.txt") as f:
for line in f.readlines():
self.gone_packages_set.add(line.strip())
return key in self.gone_packages_set
def has_scmsync(self, key):
if key in self.scmsync_cache:
return self.scmsync_cache[key]
root = self.obs._meta(key)
scmsync_exists = False
if root is not None:
scmsync_exists = root.find('scmsync') is not None
self.scmsync_cache[key] = scmsync_exists
return scmsync_exists
def package_with_scmsync(self, package):
return package in self.packages_with_scmsync
missing_user.import_into_db(db)
self.fill_file_lists(db)
db.conn.commit()

View File

@ -1,194 +0,0 @@
from __future__ import annotations
import logging
import os
import sys
import requests
from lib.binary import is_text_mimetype
from lib.db import DB
# no need for this class yet, so just leave the migration code here
class LFSOid:
def __init__(self, db: DB) -> None:
self.db = db
self.dbid = None
self.project = None
self.package = None
self.filename = None
self.revision = None
self.sha = None
self.size = None
self.mimetype = None
self.file_md5 = None
@staticmethod
def check_all(db, package):
with db.cursor() as cur:
cur.execute(
"SELECT lfs_oid_id FROM lfs_oid_in_package WHERE package=%s ORDER BY lfs_oid_id DESC limit 10 ",
(package,),
)
for row in cur.fetchall():
oid = LFSOid(db).set_from_dbid(row[0])
if not oid.check():
oid.register()
def add(
self,
project: str,
package: str,
filename: str,
revision: str,
sha256: str,
size: int,
mimetype: str,
file_md5: str,
) -> None:
with self.db.cursor() as cur:
# we UPDATE here so the return functions. conflicts are likely as we look for filename/md5 but conflict on sha256
cur.execute(
"""INSERT INTO lfs_oids (project,package,filename,rev,sha256,size,mimetype,file_md5)
VALUES (%s,%s,%s,%s,%s,%s,%s,%s)
ON CONFLICT (sha256,size) DO UPDATE SET mimetype=EXCLUDED.mimetype
RETURNING id""",
(
project,
package,
filename,
revision,
sha256,
size,
mimetype,
file_md5,
),
)
row = cur.fetchone()
lfs_oid_id = row[0]
cur.execute(
"""INSERT INTO lfs_oid_in_package (package,filename,lfs_oid_id)
VALUES (%s,%s,%s)""",
(package, filename, lfs_oid_id),
)
if is_text_mimetype(mimetype):
cur.execute(
"INSERT INTO text_files (package,filename) VALUES (%s,%s)",
(package, filename),
)
self.db.conn.commit()
self.set_from_dbid(lfs_oid_id)
if not self.check():
self.register()
def check(self):
url = f"http://localhost:9999/check/{self.sha256}/{self.size}"
response = requests.get(
url,
timeout=10,
)
return response.status_code == 200
def set_from_dbid(self, dbid: int) -> LFSOid:
with self.db.cursor() as cur:
cur.execute("SELECT * from lfs_oids where id=%s", (dbid,))
row = cur.fetchone()
self.set_from_row(row)
assert self.dbid == dbid
return self
def set_from_row(self, row: list) -> LFSOid:
(
self.dbid,
self.project,
self.package,
self.filename,
self.revision,
self.sha256,
self.size,
self.mimetype,
self.file_md5,
) = row
return self
def register(self):
if not os.getenv("GITEA_REGISTER_SECRET"):
logging.info("Not registering LFS due to missing secret")
return
data = {
"secret": os.getenv("GITEA_REGISTER_SECRET"),
"project": self.project,
"package": self.package,
"filename": self.filename,
"rev": self.revision,
"sha256": self.sha256,
"size": self.size,
}
url = "http://localhost:9999/register"
response = requests.post(
url,
json=data,
timeout=10,
)
response.raise_for_status()
logging.info(f"Register LFS returned {response.status_code}")
if __name__ == "__main__":
"""
Import the old data - it only makes sense on a DB with previously scanned revisions
curl -s https://stephan.kulow.org/git_lfs.csv.xz | xz -cd | PYTHONPATH=$PWD /usr/bin/python3 lib/lfs_oid.py
"""
db = DB()
logging.basicConfig(level=logging.DEBUG)
with db.cursor() as cur:
while True:
line = sys.stdin.readline()
if not line:
break
(
project,
package,
filename,
rev,
sha256,
size,
mimetype,
md5,
) = line.strip().split("\t")
cur.execute(
"""INSERT INTO lfs_oids (project,package,filename,rev,sha256,size,mimetype,file_md5)
VALUES (%s,%s,%s,%s,%s,%s,%s,%s) ON CONFLICT DO NOTHING""",
(project, package, filename, rev, sha256, size, mimetype, md5),
)
cur.execute(
"""
CREATE TEMPORARY TABLE lfs_oid_in_revision (
revision_id INTEGER,
lfs_oid_id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL
)
"""
)
cur.execute(
"""INSERT INTO lfs_oid_in_revision (revision_id, lfs_oid_id, name)
SELECT revision_id,lfs_oids.id,files.name FROM lfs_oids JOIN files ON files.md5=lfs_oids.file_md5"""
)
cur.execute(
"""INSERT INTO text_files (package,filename)
SELECT DISTINCT r.package, lfs_oid_in_revision.name FROM lfs_oids
JOIN lfs_oid_in_revision on lfs_oid_in_revision.lfs_oid_id=lfs_oids.id
JOIN revisions r ON r.id=lfs_oid_in_revision.revision_id
WHERE lfs_oids.mimetype like 'text/%' ON CONFLICT DO NOTHING"""
)
cur.execute(
"""INSERT INTO lfs_oid_in_package (lfs_oid_id, package, filename)
SELECT DISTINCT lfs_oids.id,r.package, lfs_oid_in_revision.name FROM lfs_oids
JOIN lfs_oid_in_revision on lfs_oid_in_revision.lfs_oid_id=lfs_oids.id
JOIN revisions r ON r.id=lfs_oid_in_revision.revision_id"""
)
db.conn.commit()

View File

@ -1,7 +1,5 @@
import errno
import logging
import os
import shutil
import time
import urllib.parse
import xml.etree.ElementTree as ET
@ -9,7 +7,6 @@ from urllib.error import HTTPError
import osc.core
from lib.hash import md5
from lib.request import Request
from lib.user import User
@ -59,25 +56,24 @@ osc.core.http_GET = retry(osc.core.http_GET)
class OBS:
def __init__(self, url):
self.url = None
self.change_url(url)
def __init__(self, url=None):
if url:
self.change_url(url)
def change_url(self, url):
if url != self.url:
self.url = url
osc.conf.get_config(override_apiurl=url)
self.url = url
osc.conf.get_config(override_apiurl=url)
def _xml(self, url_path, **params):
url = osc.core.makeurl(self.url, [url_path], params)
logging.debug(f"GET {url}")
return ET.parse(osc.core.http_GET(url)).getroot()
def _meta(self, key, **params):
def _meta(self, project, package, **params):
try:
root = self._xml(f"source/{key}/_meta", **params)
root = self._xml(f"source/{project}/{package}/_meta", **params)
except HTTPError:
logging.error(f"Project/Package [{key} {params}] has no meta")
logging.error(f"Package [{project}/{package} {params}] has no meta")
return None
return root
@ -118,13 +114,13 @@ class OBS:
return root
def exists(self, project, package):
root = self._meta(f"{project}/{package}")
root = self._meta(project, package)
if root is None:
return False
return root.get("project") == project
def devel_project(self, project, package):
root = self._meta(f"{project}/{package}")
root = self._meta(project, package)
devel = root.find("devel")
if devel is None:
return None
@ -150,7 +146,7 @@ class OBS:
def _download(self, project, package, name, revision):
url = osc.core.makeurl(
self.url,
["source", project, package, name],
["source", project, package, urllib.parse.quote(name)],
{"rev": revision, "expand": 1},
)
return osc.core.http_GET(url)
@ -162,24 +158,10 @@ class OBS:
name: str,
revision: str,
dirpath: str,
cachedir: str,
file_md5: str,
) -> None:
cached_file = self._path_from_md5(name, cachedir, file_md5)
if not self.in_cache(name, cachedir, file_md5):
with (dirpath / name).open("wb") as f:
logging.debug(f"Download {project}/{package}/{name}")
f.write(self._download(project, package, name, revision).read())
# Validate the MD5 of the downloaded file
if md5(dirpath / name) != file_md5:
raise Exception(f"Download error in {name}")
shutil.copy(dirpath / name, cached_file.with_suffix(".new"))
os.rename(cached_file.with_suffix(".new"), cached_file)
else:
shutil.copy(cached_file, dirpath / name)
logging.debug(f"Use cached {project}/{package}/{name}")
with (dirpath / name).open("wb") as f:
f.write(self._download(project, package, name, revision).read())
def list(self, project, package, srcmd5, linkrev):
params = {"rev": srcmd5, "expand": "1"}
@ -197,11 +179,3 @@ class OBS:
raise e
return root
def _path_from_md5(self, name, cachedir, md5):
filepath = cachedir / md5[:3]
filepath.mkdir(parents=True, exist_ok=True)
return filepath / md5[3:]
def in_cache(self, name, cachedir, md5):
return self._path_from_md5(name, cachedir, md5).exists()

View File

@ -1,89 +1,106 @@
import functools
import hashlib
import logging
import urllib
try:
import magic
except:
print("Install python3-python-magic, not python3-magic")
raise
import requests
from lib.db import DB
from lib.lfs_oid import LFSOid
from lib.obs import OBS
def _hash(hash_alg, file_or_path):
h = hash_alg()
def __hash(f):
while chunk := f.read(1024 * 4):
h.update(chunk)
if hasattr(file_or_path, "read"):
__hash(file_or_path)
else:
with file_or_path.open("rb") as f:
__hash(f)
return h.hexdigest()
md5 = functools.partial(_hash, hashlib.md5)
sha256 = functools.partial(_hash, hashlib.sha256)
class ProxySHA256:
def __init__(self, obs: OBS, db: DB):
def __init__(self, obs, url=None, enabled=True):
self.obs = obs
self.db = db
self.url = url if url else "http://source.dyn.cloud.suse.de"
self.enabled = enabled
self.hashes = None
self.texts = None
self.mime = None
self.texts = set()
def load_package(self, package):
# _project is unreachable for the proxy - due to being a fake package
if package == "_project":
self.enabled = False
self.texts = set(["_config", "_service"])
self.hashes = dict()
return
logging.info("Retrieve all previously defined SHA256")
response = requests.get(f"http://source.dyn.cloud.suse.de/package/{package}")
if response.status_code == 200:
json = response.json()
self.hashes = json["shas"]
self.texts = set(json["texts"])
def get(self, package, name, file_md5):
if self.hashes is None:
self.load_hashes(package)
key = f"{file_md5}-{name}"
ret = self.hashes.get(key)
return ret
if self.hashes is None:
if self.enabled:
self.load_package(package)
else:
self.hashes = {}
return self.hashes.get(key, None)
def load_hashes(self, package):
with self.db.cursor() as cur:
cur.execute(
"""SELECT lfs_oids.file_md5,lop.filename,lfs_oids.sha256,lfs_oids.size
FROM lfs_oid_in_package lop
JOIN lfs_oids ON lfs_oids.id=lop.lfs_oid_id
WHERE lop.package=%s""",
(package,),
)
self.hashes = {
f"{row[0]}-{row[1]}": (row[2], row[3]) for row in cur.fetchall()
}
def _proxy_put(self, project, package, name, revision, file_md5, size):
quoted_name = urllib.parse.quote(name)
url = f"{self.obs.url}/public/source/{project}/{package}/{quoted_name}?rev={revision}"
response = requests.put(
self.url,
data={
"hash": file_md5,
"filename": name,
"url": url,
"package": package,
},
)
if response.status_code != 200:
raise Exception(f"Redirector error on {self.url} for {url}")
key = (file_md5, name)
self.hashes[key] = {
"sha256": response.content.decode("utf-8"),
"fsize": size,
}
return self.hashes[key]
def _obs_put(self, project, package, name, revision, file_md5, size):
key = (file_md5, name)
self.hashes[key] = {
"sha256": sha256(self.obs._download(project, package, name, revision)),
"fsize": size,
}
return self.hashes[key]
def put(self, project, package, name, revision, file_md5, size):
if not self.mime:
self.mime = magic.Magic(mime=True)
if not self.enabled:
return self._obs_put(project, package, name, revision, file_md5, size)
return self._proxy_put(project, package, name, revision, file_md5, size)
mimetype = None
logging.debug(f"Add LFS for {project}/{package}/{name}")
fin = self.obs._download(project, package, name, revision)
sha = hashlib.sha256()
while True:
buffer = fin.read(10000)
if not buffer:
break
sha.update(buffer)
# only guess from the first 10K
if not mimetype:
mimetype = self.mime.from_buffer(buffer)
fin.close()
LFSOid(self.db).add(
project, package, name, revision, sha.hexdigest(), size, mimetype, file_md5
)
# reset
self.hashes = None
self.texts = None
return self.get(package, name, file_md5)
def is_text(self, package, filename):
if self.texts is None:
self.load_texts(package)
def is_text(self, filename):
return filename in self.texts
def load_texts(self, package):
self.texts = set()
with self.db.cursor() as cur:
cur.execute("SELECT filename from text_files where package=%s", (package,))
for row in cur.fetchall():
self.texts.add(row[0])
def get_or_put(self, project, package, name, revision, file_md5, size):
result = self.get(package, name, file_md5)
if not result:
result = self.put(project, package, name, revision, file_md5, size)
sha256, db_size = result
assert db_size == size
# Sanity check
if result["fsize"] != size:
raise Exception(f"Redirector has different size for {name}")
return sha256
return result

View File

@ -16,11 +16,11 @@ class TestExporter:
db = DB()
with db.cursor() as cur:
cur.execute(
"SELECT * from revisions where package=%s ORDER BY commit_time",
"SELECT * from revisions where package=%s ORDER BY project,rev",
(self.package,),
)
data = {"revisions": []}
for row in cur.fetchall():
data["revisions"].append(DBRevision(db, row).as_dict())
data["revisions"].append(DBRevision(row).as_dict(db))
yaml.dump(data, sys.stdout, default_flow_style=False)

View File

@ -1,3 +1,4 @@
from typing import Dict
from xmlrpc.client import Boolean
from lib.db_revision import DBRevision
@ -103,24 +104,14 @@ class TreeBuilder:
"""For a given revision in the target, find the node in the source chain
that matches the files"""
node = source_chain
candidates = []
while node:
# exclude reverts happening after the merge
if (
node.revision.commit_time <= revision.commit_time
and node.revision.files_hash == revision.files_hash
):
candidates.append(node)
if node.merged_into:
# we can't have candidates that are crossing previous merges
# see https://src.opensuse.org/importers/git-importer/issues/14
candidates = []
return node
node = node.parent
if candidates:
# the first candidate is the youngest one that matches the check. That's
# good enough. See FastCGI test case for rev 36 and 38: 37 reverted 36 and
# then 38 reverting the revert before it was submitted.
return candidates[0]
def add_merge_points(self, factory_revisions):
"""For all target revisions that accepted a request, look up the merge
@ -137,7 +128,7 @@ class TreeBuilder:
self.requests.add(node.revision.request_id)
class FindMergeWalker(AbstractWalker):
def __init__(self, builder: TreeBuilder, requests: dict) -> None:
def __init__(self, builder: TreeBuilder, requests: Dict) -> None:
super().__init__()
self.source_revisions = dict()
self.builder = builder

View File

@ -1,7 +1,3 @@
from __future__ import annotations
from lib.db import DB
FAKE_ACCOUNTS = (
"unknown",
"buildservice-autocommit",
@ -19,22 +15,6 @@ FAKE_ACCOUNTS = (
class User:
@staticmethod
def find(db: DB, userid: str) -> User:
row = User.lookup(db, userid)
self = User()
self.userid = userid
if row:
(_, _, self.email, self.realname) = row
else:
self.email = ""
self.realname = ""
if not self.email:
self.email = "null@suse.de"
if not self.realname:
self.realname = f"OBS User {userid}"
return self
def parse(self, xml, userid):
self.userid = userid
self.realname = xml.find("realname").text

View File

@ -1,59 +0,0 @@
#!/usr/bin/python3
import json
from pathlib import Path
import pika
import random
import time
MY_TASKS_DIR = Path(__file__).parent / "tasks"
def listen_events():
connection = pika.BlockingConnection(
pika.URLParameters("amqps://opensuse:opensuse@rabbit.opensuse.org")
)
channel = connection.channel()
channel.exchange_declare(
exchange="pubsub", exchange_type="topic", passive=True, durable=False
)
result = channel.queue_declare("", exclusive=True)
queue_name = result.method.queue
channel.queue_bind(
exchange="pubsub", queue=queue_name, routing_key="opensuse.obs.package.commit"
)
print(" [*] Waiting for logs. To exit press CTRL+C")
def callback(ch, method, properties, body):
if method.routing_key not in ("opensuse.obs.package.commit",):
return
body = json.loads(body)
if (
"project" in body
and "package" in body
and body["project"] == "openSUSE:Factory"
):
if "/" in body["package"]:
return
(MY_TASKS_DIR / body["package"]).touch()
print(" [x] %r:%r" % (method.routing_key, body["package"]))
channel.basic_consume(queue_name, callback, auto_ack=True)
channel.start_consuming()
def main():
while True:
try:
listen_events()
except (pika.exceptions.ConnectionClosed, pika.exceptions.AMQPHeartbeatTimeout):
time.sleep(random.randint(10, 100))
if __name__ == "__main__":
main()

14502
packages

File diff suppressed because it is too large Load Diff

1
tasks/.gitignore vendored
View File

@ -1 +0,0 @@
*

View File

@ -6,14 +6,11 @@ from lib.db_revision import DBRevision
from lib.obs import OBS
from lib.obs_revision import OBSRevision
# needs to exist in local oscrc (little tricky)
API_URL = "https://api.opensuse.org"
class TestDBMethods(unittest.TestCase):
def setUp(self):
self.db = DB(section="test")
self.obs = OBS(API_URL)
self.obs = OBS()
def test_import(self):
test_rev = OBSRevision(self.obs, "openSUSE:Factory", "xz")
@ -33,7 +30,6 @@ class TestDBMethods(unittest.TestCase):
db_rev = DBRevision.fetch_revision(
self.db, project="openSUSE:Factory", package="xz", rev="70"
)
self.assertEqual(db_rev.api_url, API_URL)
self.assertEqual(str(test_rev), str(db_rev))

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +0,0 @@
- factory c:openSUSE:Factory/FastCGI/29.0 p1:openSUSE:Factory/FastCGI/28.0 p2:devel:libraries:c_c++/FastCGI/40.0
- devel c:devel:libraries:c_c++/FastCGI/40.0 p1:devel:libraries:c_c++/FastCGI/38.0
- factory c:openSUSE:Factory/FastCGI/28.0 p1:openSUSE:Factory/FastCGI/27.0 p2:devel:libraries:c_c++/FastCGI/38.0
- devel c:devel:libraries:c_c++/FastCGI/38.0 p1:devel:libraries:c_c++/FastCGI/37.0
- devel c:devel:libraries:c_c++/FastCGI/37.0 p1:devel:libraries:c_c++/FastCGI/36.0
- devel c:devel:libraries:c_c++/FastCGI/36.0 p1:devel:libraries:c_c++/FastCGI/34.0
- factory c:openSUSE:Factory/FastCGI/27.0 p1:openSUSE:Factory/FastCGI/26.0 p2:devel:libraries:c_c++/FastCGI/34.0
- devel c:devel:libraries:c_c++/FastCGI/34.0 p1:devel:libraries:c_c++/FastCGI/32.0
- factory c:openSUSE:Factory/FastCGI/26.0 p1:openSUSE:Factory/FastCGI/23.0 p2:devel:libraries:c_c++/FastCGI/32.0
- devel c:devel:libraries:c_c++/FastCGI/32.0 p1:devel:libraries:c_c++/FastCGI/30.0
- factory c:openSUSE:Factory/FastCGI/23.0 p1:openSUSE:Factory/FastCGI/20.0 p2:devel:libraries:c_c++/FastCGI/30.0
- devel c:devel:libraries:c_c++/FastCGI/30.0 p1:devel:libraries:c_c++/FastCGI/28.0
- factory c:openSUSE:Factory/FastCGI/20.0 p1:openSUSE:Factory/FastCGI/19.0 p2:devel:libraries:c_c++/FastCGI/28.0
- devel c:devel:libraries:c_c++/FastCGI/28.0 p1:devel:libraries:c_c++/FastCGI/26.0
- factory c:openSUSE:Factory/FastCGI/19.0 p1:openSUSE:Factory/FastCGI/18.0 p2:devel:libraries:c_c++/FastCGI/26.0
- devel c:devel:libraries:c_c++/FastCGI/26.0 p1:devel:libraries:c_c++/FastCGI/24.0
- factory c:openSUSE:Factory/FastCGI/18.0 p1:openSUSE:Factory/FastCGI/16.0 p2:devel:libraries:c_c++/FastCGI/24.0
- devel c:devel:libraries:c_c++/FastCGI/24.0 p1:devel:libraries:c_c++/FastCGI/22.0
- factory c:openSUSE:Factory/FastCGI/16.0 p1:openSUSE:Factory/FastCGI/15.0 p2:devel:libraries:c_c++/FastCGI/22.0
- devel c:devel:libraries:c_c++/FastCGI/22.0 p1:devel:libraries:c_c++/FastCGI/20.0
- factory c:openSUSE:Factory/FastCGI/15.0 p1:openSUSE:Factory/FastCGI/14.0 p2:devel:libraries:c_c++/FastCGI/20.0
- devel c:devel:libraries:c_c++/FastCGI/20.0 p1:devel:libraries:c_c++/FastCGI/19.014
- devel c:devel:libraries:c_c++/FastCGI/19.014 p1:devel:libraries:c_c++/FastCGI/18.0
- factory c:openSUSE:Factory/FastCGI/14.0 p1:openSUSE:Factory/FastCGI/13.0
- factory c:openSUSE:Factory/FastCGI/13.0 p1:openSUSE:Factory/FastCGI/11.0 p2:devel:libraries:c_c++/FastCGI/18.0
- devel c:devel:libraries:c_c++/FastCGI/18.0 p1:openSUSE:Factory/FastCGI/11.0
- factory c:openSUSE:Factory/FastCGI/11.0 p1:openSUSE:Factory/FastCGI/10.0
- factory c:openSUSE:Factory/FastCGI/10.0 p1:openSUSE:Factory/FastCGI/7.0
- factory c:openSUSE:Factory/FastCGI/7.0 p1:openSUSE:Factory/FastCGI/6.0
- factory c:openSUSE:Factory/FastCGI/6.0 p1:openSUSE:Factory/FastCGI/4.0
- factory c:openSUSE:Factory/FastCGI/4.0 p1:openSUSE:Factory/FastCGI/3.0
- factory c:openSUSE:Factory/FastCGI/3.0 p1:openSUSE:Factory/FastCGI/1.0
- factory c:openSUSE:Factory/FastCGI/1.0

View File

@ -1,44 +0,0 @@
- commit: openSUSE:Factory/FastCGI/29.0
merged:
- devel:libraries:c_c++/FastCGI/40.0
- commit: openSUSE:Factory/FastCGI/28.0
merged:
- devel:libraries:c_c++/FastCGI/38.0
- devel:libraries:c_c++/FastCGI/37.0
- devel:libraries:c_c++/FastCGI/36.0
- commit: openSUSE:Factory/FastCGI/27.0
merged:
- devel:libraries:c_c++/FastCGI/34.0
- commit: openSUSE:Factory/FastCGI/26.0
merged:
- devel:libraries:c_c++/FastCGI/32.0
- commit: openSUSE:Factory/FastCGI/23.0
merged:
- devel:libraries:c_c++/FastCGI/30.0
- commit: openSUSE:Factory/FastCGI/20.0
merged:
- devel:libraries:c_c++/FastCGI/28.0
- commit: openSUSE:Factory/FastCGI/19.0
merged:
- devel:libraries:c_c++/FastCGI/26.0
- commit: openSUSE:Factory/FastCGI/18.0
merged:
- devel:libraries:c_c++/FastCGI/24.0
- commit: openSUSE:Factory/FastCGI/16.0
merged:
- devel:libraries:c_c++/FastCGI/22.0
- commit: openSUSE:Factory/FastCGI/15.0
merged:
- devel:libraries:c_c++/FastCGI/20.0
- devel:libraries:c_c++/FastCGI/19.014
- commit: openSUSE:Factory/FastCGI/14.0
- commit: openSUSE:Factory/FastCGI/13.0
merged:
- devel:libraries:c_c++/FastCGI/18.0
- commit: openSUSE:Factory/FastCGI/11.0
- commit: openSUSE:Factory/FastCGI/10.0
- commit: openSUSE:Factory/FastCGI/7.0
- commit: openSUSE:Factory/FastCGI/6.0
- commit: openSUSE:Factory/FastCGI/4.0
- commit: openSUSE:Factory/FastCGI/3.0
- commit: openSUSE:Factory/FastCGI/1.0

File diff suppressed because it is too large Load Diff

View File

@ -1,171 +0,0 @@
- factory c:openSUSE:Factory/breeze/43.0 p1:openSUSE:Factory/breeze/42.0 p2:KDE:Frameworks5/breeze/150.0
- devel c:KDE:Frameworks5/breeze/150.0 p1:KDE:Frameworks5/breeze/148.0
- factory c:openSUSE:Factory/breeze/42.0 p1:openSUSE:Factory/breeze/41.0 p2:KDE:Frameworks5/breeze/148.0
- devel c:KDE:Frameworks5/breeze/148.0 p1:KDE:Frameworks5/breeze/147.0
- devel c:KDE:Frameworks5/breeze/147.0 p1:KDE:Frameworks5/breeze/145.0
- factory c:openSUSE:Factory/breeze/41.0 p1:openSUSE:Factory/breeze/40.0 p2:KDE:Frameworks5/breeze/145.0
- devel c:KDE:Frameworks5/breeze/145.0 p1:KDE:Frameworks5/breeze/143.0
- factory c:openSUSE:Factory/breeze/40.0 p1:openSUSE:Factory/breeze/39.0 p2:KDE:Frameworks5/breeze/143.0
- devel c:KDE:Frameworks5/breeze/143.0 p1:KDE:Frameworks5/breeze/142.0
- devel c:KDE:Frameworks5/breeze/142.0 p1:KDE:Frameworks5/breeze/141.0
- devel c:KDE:Frameworks5/breeze/141.0 p1:KDE:Frameworks5/breeze/139.0
- factory c:openSUSE:Factory/breeze/39.0 p1:openSUSE:Factory/breeze/38.0 p2:KDE:Frameworks5/breeze/139.0
- devel c:KDE:Frameworks5/breeze/139.0 p1:KDE:Frameworks5/breeze/137.0
- factory c:openSUSE:Factory/breeze/38.0 p1:openSUSE:Factory/breeze/37.0 p2:KDE:Frameworks5/breeze/137.0
- devel c:KDE:Frameworks5/breeze/137.0 p1:KDE:Frameworks5/breeze/136.0
- devel c:KDE:Frameworks5/breeze/136.0 p1:KDE:Frameworks5/breeze/135.0
- devel c:KDE:Frameworks5/breeze/135.0 p1:KDE:Frameworks5/breeze/134.0
- devel c:KDE:Frameworks5/breeze/134.0 p1:KDE:Frameworks5/breeze/132.0
- factory c:openSUSE:Factory/breeze/37.0 p1:openSUSE:Factory/breeze/36.0 p2:KDE:Frameworks5/breeze/132.0
- devel c:KDE:Frameworks5/breeze/132.0 p1:KDE:Frameworks5/breeze/130.0
- factory c:openSUSE:Factory/breeze/36.0 p1:openSUSE:Factory/breeze/35.0 p2:KDE:Frameworks5/breeze/130.0
- devel c:KDE:Frameworks5/breeze/130.0 p1:KDE:Frameworks5/breeze/128.0
- factory c:openSUSE:Factory/breeze/35.0 p1:openSUSE:Factory/breeze/34.0 p2:KDE:Frameworks5/breeze/128.0
- devel c:KDE:Frameworks5/breeze/128.0 p1:KDE:Frameworks5/breeze/127.0
- devel c:KDE:Frameworks5/breeze/127.0 p1:KDE:Frameworks5/breeze/126.034
- devel c:KDE:Frameworks5/breeze/126.034 p1:KDE:Frameworks5/breeze/126.0
- devel c:KDE:Frameworks5/breeze/126.0 p1:KDE:Frameworks5/breeze/125.0
- devel c:KDE:Frameworks5/breeze/125.0 p1:KDE:Frameworks5/breeze/124.0
- devel c:KDE:Frameworks5/breeze/124.0 p1:KDE:Frameworks5/breeze/123.0
- devel c:KDE:Frameworks5/breeze/123.0 p1:KDE:Frameworks5/breeze/122.0
- devel c:KDE:Frameworks5/breeze/122.0 p1:KDE:Frameworks5/breeze/120.0
- factory c:openSUSE:Factory/breeze/34.0 p1:openSUSE:Factory/breeze/33.0 p2:KDE:Frameworks5:LTS/breeze/14.0
- devel c:KDE:Frameworks5:LTS/breeze/14.0 p1:KDE:Frameworks5:LTS/breeze/13.0
- devel c:KDE:Frameworks5:LTS/breeze/13.0 p1:KDE:Frameworks5:LTS/breeze/12.0
- devel c:KDE:Frameworks5:LTS/breeze/12.0 p1:KDE:Frameworks5:LTS/breeze/11.0
- factory c:openSUSE:Factory/breeze/33.0 p1:openSUSE:Factory/breeze/32.0 p2:KDE:Frameworks5:LTS/breeze/11.0
- devel c:KDE:Frameworks5:LTS/breeze/11.0 p1:KDE:Frameworks5:LTS/breeze/10.0
- devel c:KDE:Frameworks5:LTS/breeze/10.0 p1:KDE:Frameworks5:LTS/breeze/9.0
- devel c:KDE:Frameworks5:LTS/breeze/9.0 p1:KDE:Frameworks5:LTS/breeze/8.0
- devel c:KDE:Frameworks5:LTS/breeze/8.0 p1:KDE:Frameworks5:LTS/breeze/7.0
- devel c:KDE:Frameworks5:LTS/breeze/7.0 p1:KDE:Frameworks5:LTS/breeze/6.0
- devel c:KDE:Frameworks5:LTS/breeze/6.0 p1:KDE:Frameworks5:LTS/breeze/5.0
- devel c:KDE:Frameworks5:LTS/breeze/5.0 p1:KDE:Frameworks5:LTS/breeze/4.0
- devel c:KDE:Frameworks5:LTS/breeze/4.0 p1:KDE:Frameworks5:LTS/breeze/3.0
- devel c:KDE:Frameworks5:LTS/breeze/3.0 p1:KDE:Frameworks5:LTS/breeze/2.0
- devel c:KDE:Frameworks5:LTS/breeze/2.0 p1:KDE:Frameworks5:LTS/breeze/1.0
- devel c:KDE:Frameworks5:LTS/breeze/1.0 p1:openSUSE:Factory/breeze/32.0
- factory c:openSUSE:Factory/breeze/32.0 p1:openSUSE:Factory/breeze/31.0 p2:KDE:Frameworks5/breeze/120.0
- devel c:KDE:Frameworks5/breeze/120.0 p1:KDE:Frameworks5/breeze/117.0
- factory c:openSUSE:Factory/breeze/31.0 p1:openSUSE:Factory/breeze/30.0 p2:KDE:Frameworks5/breeze/117.0
- devel c:KDE:Frameworks5/breeze/117.0 p1:KDE:Frameworks5/breeze/116.0
- factory c:openSUSE:Factory/breeze/30.0 p1:openSUSE:Factory/breeze/29.0 p2:KDE:Frameworks5/breeze/116.0
- devel c:KDE:Frameworks5/breeze/116.0 p1:KDE:Frameworks5/breeze/115.0
- devel c:KDE:Frameworks5/breeze/115.0 p1:KDE:Frameworks5/breeze/113.0
- devel c:KDE:Frameworks5/breeze/113.0 p1:KDE:Frameworks5/breeze/112.0
- devel c:KDE:Frameworks5/breeze/112.0 p1:KDE:Frameworks5/breeze/111.0
- factory c:openSUSE:Factory/breeze/29.0 p1:openSUSE:Factory/breeze/28.0 p2:KDE:Frameworks5/breeze/111.0
- devel c:KDE:Frameworks5/breeze/111.0 p1:KDE:Frameworks5/breeze/110.0
- devel c:KDE:Frameworks5/breeze/110.0 p1:KDE:Frameworks5/breeze/109.0
- devel c:KDE:Frameworks5/breeze/109.0 p1:KDE:Frameworks5/breeze/108.0
- devel c:KDE:Frameworks5/breeze/108.0 p1:KDE:Frameworks5/breeze/107.0
- devel c:KDE:Frameworks5/breeze/107.0 p1:KDE:Frameworks5/breeze/105.0
- factory c:openSUSE:Factory/breeze/28.0 p1:openSUSE:Factory/breeze/27.0 p2:KDE:Frameworks5/breeze/105.0
- devel c:KDE:Frameworks5/breeze/105.0 p1:KDE:Frameworks5/breeze/103.0
- factory c:openSUSE:Factory/breeze/27.0 p1:openSUSE:Factory/breeze/26.0 p2:KDE:Frameworks5/breeze/103.0
- devel c:KDE:Frameworks5/breeze/103.0 p1:KDE:Frameworks5/breeze/100.0
- factory c:openSUSE:Factory/breeze/26.0 p1:openSUSE:Factory/breeze/25.0 p2:KDE:Frameworks5/breeze/100.0
- devel c:KDE:Frameworks5/breeze/100.0 p1:KDE:Frameworks5/breeze/99.0
- factory c:openSUSE:Factory/breeze/25.0 p1:openSUSE:Factory/breeze/24.0 p2:KDE:Frameworks5/breeze/99.0
- devel c:KDE:Frameworks5/breeze/99.0 p1:KDE:Frameworks5/breeze/98.0
- devel c:KDE:Frameworks5/breeze/98.0 p1:KDE:Frameworks5/breeze/97.0
- devel c:KDE:Frameworks5/breeze/97.0 p1:KDE:Frameworks5/breeze/95.0
- factory c:openSUSE:Factory/breeze/24.0 p1:openSUSE:Factory/breeze/23.0 p2:KDE:Frameworks5/breeze/95.0
- devel c:KDE:Frameworks5/breeze/95.0 p1:KDE:Frameworks5/breeze/93.0
- factory c:openSUSE:Factory/breeze/23.0 p1:openSUSE:Factory/breeze/22.0 p2:KDE:Frameworks5/breeze/93.0
- devel c:KDE:Frameworks5/breeze/93.0 p1:KDE:Frameworks5/breeze/91.0
- factory c:openSUSE:Factory/breeze/22.0 p1:openSUSE:Factory/breeze/21.0 p2:KDE:Frameworks5/breeze/91.0
- devel c:KDE:Frameworks5/breeze/91.0 p1:KDE:Frameworks5/breeze/88.0
- factory c:openSUSE:Factory/breeze/21.0 p1:openSUSE:Factory/breeze/20.0 p2:KDE:Frameworks5/breeze/88.0
- devel c:KDE:Frameworks5/breeze/88.0 p1:KDE:Frameworks5/breeze/87.0
- factory c:openSUSE:Factory/breeze/20.0 p1:openSUSE:Factory/breeze/19.0 p2:KDE:Frameworks5/breeze/87.0
- devel c:KDE:Frameworks5/breeze/87.0 p1:KDE:Frameworks5/breeze/86.0
- devel c:KDE:Frameworks5/breeze/86.0 p1:KDE:Frameworks5/breeze/85.0
- devel c:KDE:Frameworks5/breeze/85.0 p1:KDE:Frameworks5/breeze/84.0
- devel c:KDE:Frameworks5/breeze/84.0 p1:KDE:Frameworks5/breeze/83.0
- devel c:KDE:Frameworks5/breeze/83.0 p1:KDE:Frameworks5/breeze/82.0
- devel c:KDE:Frameworks5/breeze/82.0 p1:KDE:Frameworks5/breeze/81.0
- devel c:KDE:Frameworks5/breeze/81.0 p1:KDE:Frameworks5/breeze/80.0
- devel c:KDE:Frameworks5/breeze/80.0 p1:KDE:Frameworks5/breeze/79.0
- devel c:KDE:Frameworks5/breeze/79.0 p1:KDE:Frameworks5/breeze/78.0
- devel c:KDE:Frameworks5/breeze/78.0 p1:KDE:Frameworks5/breeze/76.0
- devel c:KDE:Frameworks5/breeze/76.0 p1:KDE:Frameworks5/breeze/75.0
- factory c:openSUSE:Factory/breeze/19.0 p1:openSUSE:Factory/breeze/18.0 p2:KDE:Frameworks5/breeze/75.0
- devel c:KDE:Frameworks5/breeze/75.0 p1:KDE:Frameworks5/breeze/74.0
- devel c:KDE:Frameworks5/breeze/74.0 p1:KDE:Frameworks5/breeze/73.0
- devel c:KDE:Frameworks5/breeze/73.0 p1:KDE:Frameworks5/breeze/71.0
- factory c:openSUSE:Factory/breeze/18.0 p1:openSUSE:Factory/breeze/17.0 p2:KDE:Frameworks5/breeze/71.0
- devel c:KDE:Frameworks5/breeze/71.0 p1:KDE:Frameworks5/breeze/70.0
- devel c:KDE:Frameworks5/breeze/70.0 p1:KDE:Frameworks5/breeze/69.0
- devel c:KDE:Frameworks5/breeze/69.0 p1:KDE:Frameworks5/breeze/68.0
- devel c:KDE:Frameworks5/breeze/68.0 p1:KDE:Frameworks5/breeze/67.0
- devel c:KDE:Frameworks5/breeze/67.0 p1:KDE:Frameworks5/breeze/65.0
- factory c:openSUSE:Factory/breeze/17.0 p1:openSUSE:Factory/breeze/16.0 p2:KDE:Frameworks5/breeze/65.0
- devel c:KDE:Frameworks5/breeze/65.0 p1:KDE:Frameworks5/breeze/64.0
- devel c:KDE:Frameworks5/breeze/64.0 p1:KDE:Frameworks5/breeze/62.0
- factory c:openSUSE:Factory/breeze/16.0 p1:openSUSE:Factory/breeze/15.0 p2:KDE:Frameworks5/breeze/62.0
- devel c:KDE:Frameworks5/breeze/62.0 p1:KDE:Frameworks5/breeze/61.0
- devel c:KDE:Frameworks5/breeze/61.0 p1:KDE:Frameworks5/breeze/60.0
- devel c:KDE:Frameworks5/breeze/60.0 p1:KDE:Frameworks5/breeze/59.0
- devel c:KDE:Frameworks5/breeze/59.0 p1:KDE:Frameworks5/breeze/58.0
- devel c:KDE:Frameworks5/breeze/58.0 p1:KDE:Frameworks5/breeze/57.0
- devel c:KDE:Frameworks5/breeze/57.0 p1:KDE:Frameworks5/breeze/55.0
- factory c:openSUSE:Factory/breeze/15.0 p1:openSUSE:Factory/breeze/14.0 p2:KDE:Frameworks5/breeze/55.0
- devel c:KDE:Frameworks5/breeze/55.0 p1:KDE:Frameworks5/breeze/53.0
- factory c:openSUSE:Factory/breeze/14.0 p1:openSUSE:Factory/breeze/13.0 p2:KDE:Frameworks5/breeze/53.0
- devel c:KDE:Frameworks5/breeze/53.0 p1:KDE:Frameworks5/breeze/51.0
- factory c:openSUSE:Factory/breeze/13.0 p1:openSUSE:Factory/breeze/12.0 p2:KDE:Frameworks5/breeze/51.0
- devel c:KDE:Frameworks5/breeze/51.0 p1:KDE:Frameworks5/breeze/50.0
- devel c:KDE:Frameworks5/breeze/50.0 p1:KDE:Frameworks5/breeze/49.0
- devel c:KDE:Frameworks5/breeze/49.0 p1:KDE:Frameworks5/breeze/48.0
- devel c:KDE:Frameworks5/breeze/48.0 p1:KDE:Frameworks5/breeze/47.0
- devel c:KDE:Frameworks5/breeze/47.0 p1:KDE:Frameworks5/breeze/46.0
- devel c:KDE:Frameworks5/breeze/46.0 p1:KDE:Frameworks5/breeze/45.0
- devel c:KDE:Frameworks5/breeze/45.0 p1:KDE:Frameworks5/breeze/44.0
- devel c:KDE:Frameworks5/breeze/44.0 p1:KDE:Frameworks5/breeze/43.0
- devel c:KDE:Frameworks5/breeze/43.0 p1:KDE:Frameworks5/breeze/41.0
- factory c:openSUSE:Factory/breeze/12.0 p1:openSUSE:Factory/breeze/11.0 p2:KDE:Frameworks5/breeze/41.0
- devel c:KDE:Frameworks5/breeze/41.0 p1:KDE:Frameworks5/breeze/40.0
- devel c:KDE:Frameworks5/breeze/40.0 p1:KDE:Frameworks5/breeze/39.0
- devel c:KDE:Frameworks5/breeze/39.0 p1:KDE:Frameworks5/breeze/38.0
- factory c:openSUSE:Factory/breeze/11.0 p1:openSUSE:Factory/breeze/10.0 p2:KDE:Frameworks5/breeze/38.0
- devel c:KDE:Frameworks5/breeze/38.0 p1:KDE:Frameworks5/breeze/36.0
- factory c:openSUSE:Factory/breeze/10.0 p1:openSUSE:Factory/breeze/9.0 p2:KDE:Frameworks5/breeze/36.0
- devel c:KDE:Frameworks5/breeze/36.0 p1:KDE:Frameworks5/breeze/35.0
- devel c:KDE:Frameworks5/breeze/35.0 p1:KDE:Frameworks5/breeze/33.0
- factory c:openSUSE:Factory/breeze/9.0 p1:openSUSE:Factory/breeze/8.0 p2:KDE:Frameworks5/breeze/33.0
- devel c:KDE:Frameworks5/breeze/33.0 p1:KDE:Frameworks5/breeze/32.0
- devel c:KDE:Frameworks5/breeze/32.0 p1:KDE:Frameworks5/breeze/31.0
- devel c:KDE:Frameworks5/breeze/31.0 p1:KDE:Frameworks5/breeze/30.0
- devel c:KDE:Frameworks5/breeze/30.0 p1:KDE:Frameworks5/breeze/28.0
- factory c:openSUSE:Factory/breeze/8.0 p1:openSUSE:Factory/breeze/7.0 p2:KDE:Frameworks5/breeze/28.0
- devel c:KDE:Frameworks5/breeze/28.0 p1:KDE:Frameworks5/breeze/27.0
- devel c:KDE:Frameworks5/breeze/27.0 p1:KDE:Frameworks5/breeze/25.0
- factory c:openSUSE:Factory/breeze/7.0 p1:openSUSE:Factory/breeze/6.0 p2:KDE:Frameworks5/breeze/25.0
- devel c:KDE:Frameworks5/breeze/25.0 p1:KDE:Frameworks5/breeze/24.0
- devel c:KDE:Frameworks5/breeze/24.0 p1:KDE:Frameworks5/breeze/22.0
- factory c:openSUSE:Factory/breeze/6.0 p1:openSUSE:Factory/breeze/5.0 p2:KDE:Frameworks5/breeze/22.0
- devel c:KDE:Frameworks5/breeze/22.0 p1:KDE:Frameworks5/breeze/21.0
- devel c:KDE:Frameworks5/breeze/21.0 p1:KDE:Frameworks5/breeze/20.0
- devel c:KDE:Frameworks5/breeze/20.0 p1:KDE:Frameworks5/breeze/19.0
- devel c:KDE:Frameworks5/breeze/19.0 p1:KDE:Frameworks5/breeze/18.0
- devel c:KDE:Frameworks5/breeze/18.0 p1:KDE:Frameworks5/breeze/17.0
- factory c:openSUSE:Factory/breeze/5.0 p1:openSUSE:Factory/breeze/4.0 p2:KDE:Frameworks5/breeze/17.0
- devel c:KDE:Frameworks5/breeze/17.0 p1:KDE:Frameworks5/breeze/16.0
- devel c:KDE:Frameworks5/breeze/16.0 p1:KDE:Frameworks5/breeze/15.0
- devel c:KDE:Frameworks5/breeze/15.0 p1:KDE:Frameworks5/breeze/14.0
- devel c:KDE:Frameworks5/breeze/14.0 p1:KDE:Frameworks5/breeze/13.0
- devel c:KDE:Frameworks5/breeze/13.0 p1:KDE:Frameworks5/breeze/12.0
- devel c:KDE:Frameworks5/breeze/12.0 p1:KDE:Frameworks5/breeze/11.0
- factory c:openSUSE:Factory/breeze/4.0 p1:openSUSE:Factory/breeze/2.0 p2:KDE:Frameworks5/breeze/11.0
- devel c:KDE:Frameworks5/breeze/11.0 p1:KDE:Frameworks5/breeze/10.0
- devel c:KDE:Frameworks5/breeze/10.0 p1:KDE:Frameworks5/breeze/9.0
- devel c:KDE:Frameworks5/breeze/9.0 p1:KDE:Frameworks5/breeze/8.0
- devel c:KDE:Frameworks5/breeze/8.0 p1:KDE:Frameworks5/breeze/6.0
- factory c:openSUSE:Factory/breeze/2.0 p1:openSUSE:Factory/breeze/1.0 p2:KDE:Frameworks5/breeze/6.0
- devel c:KDE:Frameworks5/breeze/6.0 p1:openSUSE:Factory/breeze/1.0
- factory c:openSUSE:Factory/breeze/1.0 p1:KDE:Frameworks5/breeze/4.0
- factory c:KDE:Frameworks5/breeze/4.0 p1:KDE:Frameworks5/breeze/3.0
- factory c:KDE:Frameworks5/breeze/3.0 p1:KDE:Frameworks5/breeze/2.0
- factory c:KDE:Frameworks5/breeze/2.0 p1:KDE:Frameworks5/breeze/1.0
- factory c:KDE:Frameworks5/breeze/1.0

View File

@ -1,212 +0,0 @@
- commit: openSUSE:Factory/breeze/43.0
merged:
- KDE:Frameworks5/breeze/150.0
- commit: openSUSE:Factory/breeze/42.0
merged:
- KDE:Frameworks5/breeze/148.0
- KDE:Frameworks5/breeze/147.0
- commit: openSUSE:Factory/breeze/41.0
merged:
- KDE:Frameworks5/breeze/145.0
- commit: openSUSE:Factory/breeze/40.0
merged:
- KDE:Frameworks5/breeze/143.0
- KDE:Frameworks5/breeze/142.0
- KDE:Frameworks5/breeze/141.0
- commit: openSUSE:Factory/breeze/39.0
merged:
- KDE:Frameworks5/breeze/139.0
- commit: openSUSE:Factory/breeze/38.0
merged:
- KDE:Frameworks5/breeze/137.0
- KDE:Frameworks5/breeze/136.0
- KDE:Frameworks5/breeze/135.0
- KDE:Frameworks5/breeze/134.0
- commit: openSUSE:Factory/breeze/37.0
merged:
- KDE:Frameworks5/breeze/132.0
- commit: openSUSE:Factory/breeze/36.0
merged:
- KDE:Frameworks5/breeze/130.0
- commit: openSUSE:Factory/breeze/35.0
merged:
- KDE:Frameworks5/breeze/128.0
- KDE:Frameworks5/breeze/127.0
- KDE:Frameworks5/breeze/126.034
- KDE:Frameworks5/breeze/126.0
- KDE:Frameworks5/breeze/125.0
- KDE:Frameworks5/breeze/124.0
- KDE:Frameworks5/breeze/123.0
- KDE:Frameworks5/breeze/122.0
- commit: openSUSE:Factory/breeze/34.0
merged:
- KDE:Frameworks5:LTS/breeze/14.0
- KDE:Frameworks5:LTS/breeze/13.0
- KDE:Frameworks5:LTS/breeze/12.0
- commit: openSUSE:Factory/breeze/33.0
merged:
- KDE:Frameworks5:LTS/breeze/11.0
- KDE:Frameworks5:LTS/breeze/10.0
- KDE:Frameworks5:LTS/breeze/9.0
- KDE:Frameworks5:LTS/breeze/8.0
- KDE:Frameworks5:LTS/breeze/7.0
- KDE:Frameworks5:LTS/breeze/6.0
- KDE:Frameworks5:LTS/breeze/5.0
- KDE:Frameworks5:LTS/breeze/4.0
- KDE:Frameworks5:LTS/breeze/3.0
- KDE:Frameworks5:LTS/breeze/2.0
- KDE:Frameworks5:LTS/breeze/1.0
- commit: openSUSE:Factory/breeze/32.0
merged:
- KDE:Frameworks5/breeze/120.0
- commit: openSUSE:Factory/breeze/31.0
merged:
- KDE:Frameworks5/breeze/117.0
- commit: openSUSE:Factory/breeze/30.0
merged:
- KDE:Frameworks5/breeze/116.0
- KDE:Frameworks5/breeze/115.0
- KDE:Frameworks5/breeze/113.0
- KDE:Frameworks5/breeze/112.0
- commit: openSUSE:Factory/breeze/29.0
merged:
- KDE:Frameworks5/breeze/111.0
- KDE:Frameworks5/breeze/110.0
- KDE:Frameworks5/breeze/109.0
- KDE:Frameworks5/breeze/108.0
- KDE:Frameworks5/breeze/107.0
- commit: openSUSE:Factory/breeze/28.0
merged:
- KDE:Frameworks5/breeze/105.0
- commit: openSUSE:Factory/breeze/27.0
merged:
- KDE:Frameworks5/breeze/103.0
- commit: openSUSE:Factory/breeze/26.0
merged:
- KDE:Frameworks5/breeze/100.0
- commit: openSUSE:Factory/breeze/25.0
merged:
- KDE:Frameworks5/breeze/99.0
- KDE:Frameworks5/breeze/98.0
- KDE:Frameworks5/breeze/97.0
- commit: openSUSE:Factory/breeze/24.0
merged:
- KDE:Frameworks5/breeze/95.0
- commit: openSUSE:Factory/breeze/23.0
merged:
- KDE:Frameworks5/breeze/93.0
- commit: openSUSE:Factory/breeze/22.0
merged:
- KDE:Frameworks5/breeze/91.0
- commit: openSUSE:Factory/breeze/21.0
merged:
- KDE:Frameworks5/breeze/88.0
- commit: openSUSE:Factory/breeze/20.0
merged:
- KDE:Frameworks5/breeze/87.0
- KDE:Frameworks5/breeze/86.0
- KDE:Frameworks5/breeze/85.0
- KDE:Frameworks5/breeze/84.0
- KDE:Frameworks5/breeze/83.0
- KDE:Frameworks5/breeze/82.0
- KDE:Frameworks5/breeze/81.0
- KDE:Frameworks5/breeze/80.0
- KDE:Frameworks5/breeze/79.0
- KDE:Frameworks5/breeze/78.0
- KDE:Frameworks5/breeze/76.0
- commit: openSUSE:Factory/breeze/19.0
merged:
- KDE:Frameworks5/breeze/75.0
- KDE:Frameworks5/breeze/74.0
- KDE:Frameworks5/breeze/73.0
- commit: openSUSE:Factory/breeze/18.0
merged:
- KDE:Frameworks5/breeze/71.0
- KDE:Frameworks5/breeze/70.0
- KDE:Frameworks5/breeze/69.0
- KDE:Frameworks5/breeze/68.0
- KDE:Frameworks5/breeze/67.0
- commit: openSUSE:Factory/breeze/17.0
merged:
- KDE:Frameworks5/breeze/65.0
- KDE:Frameworks5/breeze/64.0
- commit: openSUSE:Factory/breeze/16.0
merged:
- KDE:Frameworks5/breeze/62.0
- KDE:Frameworks5/breeze/61.0
- KDE:Frameworks5/breeze/60.0
- KDE:Frameworks5/breeze/59.0
- KDE:Frameworks5/breeze/58.0
- KDE:Frameworks5/breeze/57.0
- commit: openSUSE:Factory/breeze/15.0
merged:
- KDE:Frameworks5/breeze/55.0
- commit: openSUSE:Factory/breeze/14.0
merged:
- KDE:Frameworks5/breeze/53.0
- commit: openSUSE:Factory/breeze/13.0
merged:
- KDE:Frameworks5/breeze/51.0
- KDE:Frameworks5/breeze/50.0
- KDE:Frameworks5/breeze/49.0
- KDE:Frameworks5/breeze/48.0
- KDE:Frameworks5/breeze/47.0
- KDE:Frameworks5/breeze/46.0
- KDE:Frameworks5/breeze/45.0
- KDE:Frameworks5/breeze/44.0
- KDE:Frameworks5/breeze/43.0
- commit: openSUSE:Factory/breeze/12.0
merged:
- KDE:Frameworks5/breeze/41.0
- KDE:Frameworks5/breeze/40.0
- KDE:Frameworks5/breeze/39.0
- commit: openSUSE:Factory/breeze/11.0
merged:
- KDE:Frameworks5/breeze/38.0
- commit: openSUSE:Factory/breeze/10.0
merged:
- KDE:Frameworks5/breeze/36.0
- KDE:Frameworks5/breeze/35.0
- commit: openSUSE:Factory/breeze/9.0
merged:
- KDE:Frameworks5/breeze/33.0
- KDE:Frameworks5/breeze/32.0
- KDE:Frameworks5/breeze/31.0
- KDE:Frameworks5/breeze/30.0
- commit: openSUSE:Factory/breeze/8.0
merged:
- KDE:Frameworks5/breeze/28.0
- KDE:Frameworks5/breeze/27.0
- commit: openSUSE:Factory/breeze/7.0
merged:
- KDE:Frameworks5/breeze/25.0
- KDE:Frameworks5/breeze/24.0
- commit: openSUSE:Factory/breeze/6.0
merged:
- KDE:Frameworks5/breeze/22.0
- KDE:Frameworks5/breeze/21.0
- KDE:Frameworks5/breeze/20.0
- KDE:Frameworks5/breeze/19.0
- KDE:Frameworks5/breeze/18.0
- commit: openSUSE:Factory/breeze/5.0
merged:
- KDE:Frameworks5/breeze/17.0
- KDE:Frameworks5/breeze/16.0
- KDE:Frameworks5/breeze/15.0
- KDE:Frameworks5/breeze/14.0
- KDE:Frameworks5/breeze/13.0
- KDE:Frameworks5/breeze/12.0
- commit: openSUSE:Factory/breeze/4.0
merged:
- KDE:Frameworks5/breeze/11.0
- KDE:Frameworks5/breeze/10.0
- KDE:Frameworks5/breeze/9.0
- KDE:Frameworks5/breeze/8.0
- commit: openSUSE:Factory/breeze/2.0
merged:
- KDE:Frameworks5/breeze/6.0
- commit: openSUSE:Factory/breeze/1.0
- commit: KDE:Frameworks5/breeze/4.0
- commit: KDE:Frameworks5/breeze/3.0
- commit: KDE:Frameworks5/breeze/2.0
- commit: KDE:Frameworks5/breeze/1.0

File diff suppressed because it is too large Load Diff

View File

@ -1,173 +0,0 @@
- factory c:openSUSE:Factory/firewalld/73.0 p1:openSUSE:Factory/firewalld/72.0 p2:security:netfilter/firewalld/131.0
- devel c:security:netfilter/firewalld/131.0 p1:security:netfilter/firewalld/129.0
- factory c:openSUSE:Factory/firewalld/72.0 p1:openSUSE:Factory/firewalld/71.0 p2:security:netfilter/firewalld/129.0
- devel c:security:netfilter/firewalld/129.0 p1:security:netfilter/firewalld/128.0
- devel c:security:netfilter/firewalld/128.0 p1:security:netfilter/firewalld/127.0
- factory c:openSUSE:Factory/firewalld/71.0 p1:openSUSE:Factory/firewalld/70.0 p2:security:netfilter/firewalld/127.0
- devel c:security:netfilter/firewalld/127.0 p1:security:netfilter/firewalld/126.0
- factory c:openSUSE:Factory/firewalld/70.0 p1:openSUSE:Factory/firewalld/69.0
- factory c:openSUSE:Factory/firewalld/69.0 p1:openSUSE:Factory/firewalld/68.0 p2:security:netfilter/firewalld/126.0
- devel c:security:netfilter/firewalld/126.0 p1:security:netfilter/firewalld/125.0
- factory c:openSUSE:Factory/firewalld/68.0 p1:openSUSE:Factory/firewalld/67.0 p2:security:netfilter/firewalld/125.0
- devel c:security:netfilter/firewalld/125.0 p1:security:netfilter/firewalld/124.0
- factory c:openSUSE:Factory/firewalld/67.0 p1:openSUSE:Factory/firewalld/66.0 p2:security:netfilter/firewalld/124.0
- devel c:security:netfilter/firewalld/124.0 p1:security:netfilter/firewalld/123.0
- factory c:openSUSE:Factory/firewalld/66.0 p1:openSUSE:Factory/firewalld/65.0 p2:security:netfilter/firewalld/123.0
- devel c:security:netfilter/firewalld/123.0 p1:security:netfilter/firewalld/122.0
- factory c:openSUSE:Factory/firewalld/65.0 p1:openSUSE:Factory/firewalld/64.0 p2:security:netfilter/firewalld/122.0
- devel c:security:netfilter/firewalld/122.0 p1:security:netfilter/firewalld/121.0
- factory c:openSUSE:Factory/firewalld/64.0 p1:openSUSE:Factory/firewalld/63.0 p2:security:netfilter/firewalld/121.0
- devel c:security:netfilter/firewalld/121.0 p1:security:netfilter/firewalld/120.0
- factory c:openSUSE:Factory/firewalld/63.0 p1:openSUSE:Factory/firewalld/62.0 p2:security:netfilter/firewalld/120.0
- devel c:security:netfilter/firewalld/120.0 p1:security:netfilter/firewalld/119.0
- factory c:openSUSE:Factory/firewalld/62.0 p1:openSUSE:Factory/firewalld/61.0 p2:security:netfilter/firewalld/119.0
- devel c:security:netfilter/firewalld/119.0 p1:security:netfilter/firewalld/118.0
- factory c:openSUSE:Factory/firewalld/61.0 p1:openSUSE:Factory/firewalld/60.0 p2:security:netfilter/firewalld/118.0
- devel c:security:netfilter/firewalld/118.0 p1:security:netfilter/firewalld/117.0
- factory c:openSUSE:Factory/firewalld/60.0 p1:openSUSE:Factory/firewalld/59.0 p2:security:netfilter/firewalld/117.0
- devel c:security:netfilter/firewalld/117.0 p1:security:netfilter/firewalld/116.0
- factory c:openSUSE:Factory/firewalld/59.0 p1:openSUSE:Factory/firewalld/58.0 p2:security:netfilter/firewalld/116.0
- devel c:security:netfilter/firewalld/116.0 p1:security:netfilter/firewalld/115.0
- factory c:openSUSE:Factory/firewalld/58.0 p1:openSUSE:Factory/firewalld/57.0 p2:security:netfilter/firewalld/115.0
- devel c:security:netfilter/firewalld/115.0 p1:security:netfilter/firewalld/114.0
- factory c:openSUSE:Factory/firewalld/57.0 p1:openSUSE:Factory/firewalld/56.0 p2:security:netfilter/firewalld/114.0
- devel c:security:netfilter/firewalld/114.0 p1:security:netfilter/firewalld/113.0
- factory c:openSUSE:Factory/firewalld/56.0 p1:openSUSE:Factory/firewalld/55.0 p2:security:netfilter/firewalld/113.0
- devel c:security:netfilter/firewalld/113.0 p1:security:netfilter/firewalld/112.0
- factory c:openSUSE:Factory/firewalld/55.0 p1:openSUSE:Factory/firewalld/54.0 p2:security:netfilter/firewalld/112.0
- devel c:security:netfilter/firewalld/112.0 p1:security:netfilter/firewalld/111.0
- devel c:security:netfilter/firewalld/111.0 p1:security:netfilter/firewalld/110.0
- devel c:security:netfilter/firewalld/110.0 p1:security:netfilter/firewalld/109.0
- devel c:security:netfilter/firewalld/109.0 p1:security:netfilter/firewalld/108.0
- factory c:openSUSE:Factory/firewalld/54.0 p1:openSUSE:Factory/firewalld/53.0 p2:security:netfilter/firewalld/108.0
- devel c:security:netfilter/firewalld/108.0 p1:security:netfilter/firewalld/107.0
- factory c:openSUSE:Factory/firewalld/53.0 p1:openSUSE:Factory/firewalld/52.0 p2:security:netfilter/firewalld/107.0
- devel c:security:netfilter/firewalld/107.0 p1:security:netfilter/firewalld/106.0
- factory c:openSUSE:Factory/firewalld/52.0 p1:openSUSE:Factory/firewalld/51.0
- factory c:openSUSE:Factory/firewalld/51.0 p1:openSUSE:Factory/firewalld/50.0
- factory c:openSUSE:Factory/firewalld/50.0 p1:openSUSE:Factory/firewalld/49.0 p2:security:netfilter/firewalld/106.0
- devel c:security:netfilter/firewalld/106.0 p1:security:netfilter/firewalld/105.0
- factory c:openSUSE:Factory/firewalld/49.0 p1:openSUSE:Factory/firewalld/48.0 p2:security:netfilter/firewalld/105.0
- devel c:security:netfilter/firewalld/105.0 p1:security:netfilter/firewalld/104.0
- devel c:security:netfilter/firewalld/104.0 p1:security:netfilter/firewalld/103.0
- devel c:security:netfilter/firewalld/103.0 p1:security:netfilter/firewalld/102.0
- factory c:openSUSE:Factory/firewalld/48.0 p1:openSUSE:Factory/firewalld/47.0 p2:security:netfilter/firewalld/102.0
- devel c:security:netfilter/firewalld/102.0 p1:security:netfilter/firewalld/101.0
- factory c:openSUSE:Factory/firewalld/47.0 p1:openSUSE:Factory/firewalld/46.0 p2:security:netfilter/firewalld/101.0
- devel c:security:netfilter/firewalld/101.0 p1:security:netfilter/firewalld/100.0
- factory c:openSUSE:Factory/firewalld/46.0 p1:openSUSE:Factory/firewalld/45.0 p2:security:netfilter/firewalld/100.0
- devel c:security:netfilter/firewalld/100.0 p1:security:netfilter/firewalld/99.0
- factory c:openSUSE:Factory/firewalld/45.0 p1:openSUSE:Factory/firewalld/44.0 p2:security:netfilter/firewalld/99.0
- devel c:security:netfilter/firewalld/99.0 p1:security:netfilter/firewalld/98.0
- factory c:openSUSE:Factory/firewalld/44.0 p1:openSUSE:Factory/firewalld/43.0 p2:security:netfilter/firewalld/98.0
- devel c:security:netfilter/firewalld/98.0 p1:security:netfilter/firewalld/97.0
- factory c:openSUSE:Factory/firewalld/43.0 p1:openSUSE:Factory/firewalld/42.0 p2:security:netfilter/firewalld/97.0
- devel c:security:netfilter/firewalld/97.0 p1:security:netfilter/firewalld/96.0
- devel c:security:netfilter/firewalld/96.0 p1:security:netfilter/firewalld/95.0
- devel c:security:netfilter/firewalld/95.0 p1:security:netfilter/firewalld/94.0
- devel c:security:netfilter/firewalld/94.0 p1:security:netfilter/firewalld/93.0
- factory c:openSUSE:Factory/firewalld/42.0 p1:openSUSE:Factory/firewalld/41.0 p2:security:netfilter/firewalld/93.0
- devel c:security:netfilter/firewalld/93.0 p1:security:netfilter/firewalld/92.0
- factory c:openSUSE:Factory/firewalld/41.0 p1:openSUSE:Factory/firewalld/40.0 p2:security:netfilter/firewalld/92.0
- devel c:security:netfilter/firewalld/92.0 p1:security:netfilter/firewalld/91.0
- factory c:openSUSE:Factory/firewalld/40.0 p1:openSUSE:Factory/firewalld/39.0 p2:security:netfilter/firewalld/91.0
- devel c:security:netfilter/firewalld/91.0 p1:security:netfilter/firewalld/90.0
- factory c:openSUSE:Factory/firewalld/39.0 p1:openSUSE:Factory/firewalld/38.0 p2:security:netfilter/firewalld/90.0
- devel c:security:netfilter/firewalld/90.0 p1:security:netfilter/firewalld/89.0
- factory c:openSUSE:Factory/firewalld/38.0 p1:openSUSE:Factory/firewalld/37.0 p2:security:netfilter/firewalld/89.0
- devel c:security:netfilter/firewalld/89.0 p1:security:netfilter/firewalld/88.0
- factory c:openSUSE:Factory/firewalld/37.0 p1:openSUSE:Factory/firewalld/36.0 p2:security:netfilter/firewalld/88.0
- devel c:security:netfilter/firewalld/88.0 p1:security:netfilter/firewalld/87.0
- devel c:security:netfilter/firewalld/87.0 p1:security:netfilter/firewalld/86.0
- devel c:security:netfilter/firewalld/86.0 p1:security:netfilter/firewalld/85.0
- devel c:security:netfilter/firewalld/85.0 p1:security:netfilter/firewalld/84.0
- factory c:openSUSE:Factory/firewalld/36.0 p1:openSUSE:Factory/firewalld/35.0 p2:security:netfilter/firewalld/84.0
- devel c:security:netfilter/firewalld/84.0 p1:security:netfilter/firewalld/83.0
- devel c:security:netfilter/firewalld/83.0 p1:security:netfilter/firewalld/82.0
- factory c:openSUSE:Factory/firewalld/35.0 p1:openSUSE:Factory/firewalld/34.0 p2:security:netfilter/firewalld/82.0
- devel c:security:netfilter/firewalld/82.0 p1:security:netfilter/firewalld/81.0
- devel c:security:netfilter/firewalld/81.0 p1:security:netfilter/firewalld/80.0
- devel c:security:netfilter/firewalld/80.0 p1:security:netfilter/firewalld/79.0
- devel c:security:netfilter/firewalld/79.0 p1:security:netfilter/firewalld/78.0
- devel c:security:netfilter/firewalld/78.0 p1:security:netfilter/firewalld/77.0
- factory c:openSUSE:Factory/firewalld/34.0 p1:openSUSE:Factory/firewalld/33.0 p2:security:netfilter/firewalld/77.0
- devel c:security:netfilter/firewalld/77.0 p1:security:netfilter/firewalld/76.0
- devel c:security:netfilter/firewalld/76.0 p1:security:netfilter/firewalld/75.0
- devel c:security:netfilter/firewalld/75.0 p1:security:netfilter/firewalld/74.0
- factory c:openSUSE:Factory/firewalld/33.0 p1:openSUSE:Factory/firewalld/32.0
- factory c:openSUSE:Factory/firewalld/32.0 p1:openSUSE:Factory/firewalld/31.0 p2:security:netfilter/firewalld/74.0
- devel c:security:netfilter/firewalld/74.0 p1:security:netfilter/firewalld/71.0
- factory c:openSUSE:Factory/firewalld/31.0 p1:openSUSE:Factory/firewalld/30.0
- factory c:openSUSE:Factory/firewalld/30.0 p1:openSUSE:Factory/firewalld/29.0 p2:security:netfilter/firewalld/71.0
- devel c:security:netfilter/firewalld/71.0 p1:security:netfilter/firewalld/69.0
- factory c:openSUSE:Factory/firewalld/29.0 p1:openSUSE:Factory/firewalld/28.0 p2:security:netfilter/firewalld/69.0
- devel c:security:netfilter/firewalld/69.0 p1:security:netfilter/firewalld/68.0
- factory c:openSUSE:Factory/firewalld/28.0 p1:openSUSE:Factory/firewalld/27.0 p2:security:netfilter/firewalld/68.0
- devel c:security:netfilter/firewalld/68.0 p1:security:netfilter/firewalld/67.0
- devel c:security:netfilter/firewalld/67.0 p1:security:netfilter/firewalld/65.0
- factory c:openSUSE:Factory/firewalld/27.0 p1:openSUSE:Factory/firewalld/26.0 p2:security:netfilter/firewalld/65.0
- devel c:security:netfilter/firewalld/65.0 p1:security:netfilter/firewalld/63.0
- factory c:openSUSE:Factory/firewalld/26.0 p1:openSUSE:Factory/firewalld/25.0 p2:security:netfilter/firewalld/63.0
- devel c:security:netfilter/firewalld/63.0 p1:security:netfilter/firewalld/61.0
- factory c:openSUSE:Factory/firewalld/25.0 p1:openSUSE:Factory/firewalld/24.0 p2:security:netfilter/firewalld/61.0
- devel c:security:netfilter/firewalld/61.0 p1:security:netfilter/firewalld/60.0
- devel c:security:netfilter/firewalld/60.0 p1:security:netfilter/firewalld/59.0
- devel c:security:netfilter/firewalld/59.0 p1:security:netfilter/firewalld/57.0
- factory c:openSUSE:Factory/firewalld/24.0 p1:openSUSE:Factory/firewalld/23.0 p2:security:netfilter/firewalld/57.0
- devel c:security:netfilter/firewalld/57.0 p1:security:netfilter/firewalld/55.0
- factory c:openSUSE:Factory/firewalld/23.0 p1:openSUSE:Factory/firewalld/22.0 p2:security:netfilter/firewalld/55.0
- devel c:security:netfilter/firewalld/55.0 p1:security:netfilter/firewalld/54.0
- devel c:security:netfilter/firewalld/54.0 p1:security:netfilter/firewalld/53.0
- devel c:security:netfilter/firewalld/53.0 p1:security:netfilter/firewalld/51.0
- factory c:openSUSE:Factory/firewalld/22.0 p1:openSUSE:Factory/firewalld/21.0 p2:security:netfilter/firewalld/51.0
- devel c:security:netfilter/firewalld/51.0 p1:security:netfilter/firewalld/50.0
- devel c:security:netfilter/firewalld/50.0 p1:security:netfilter/firewalld/48.0
- factory c:openSUSE:Factory/firewalld/21.0 p1:openSUSE:Factory/firewalld/20.0 p2:security:netfilter/firewalld/48.0
- devel c:security:netfilter/firewalld/48.0 p1:security:netfilter/firewalld/47.0
- devel c:security:netfilter/firewalld/47.0 p1:security:netfilter/firewalld/45.0
- factory c:openSUSE:Factory/firewalld/20.0 p1:openSUSE:Factory/firewalld/19.0 p2:security:netfilter/firewalld/45.0
- devel c:security:netfilter/firewalld/45.0 p1:security:netfilter/firewalld/43.0
- factory c:openSUSE:Factory/firewalld/19.0 p1:openSUSE:Factory/firewalld/18.0 p2:security:netfilter/firewalld/43.0
- devel c:security:netfilter/firewalld/43.0 p1:security:netfilter/firewalld/41.0
- factory c:openSUSE:Factory/firewalld/18.0 p1:openSUSE:Factory/firewalld/17.0 p2:security:netfilter/firewalld/41.0
- devel c:security:netfilter/firewalld/41.0 p1:security:netfilter/firewalld/39.0
- factory c:openSUSE:Factory/firewalld/17.0 p1:openSUSE:Factory/firewalld/16.0 p2:security:netfilter/firewalld/39.0
- devel c:security:netfilter/firewalld/39.0 p1:security:netfilter/firewalld/38.0
- devel c:security:netfilter/firewalld/38.0 p1:security:netfilter/firewalld/36.0
- factory c:openSUSE:Factory/firewalld/16.0 p1:openSUSE:Factory/firewalld/15.0 p2:security:netfilter/firewalld/36.0
- devel c:security:netfilter/firewalld/36.0 p1:security:netfilter/firewalld/34.0
- factory c:openSUSE:Factory/firewalld/15.0 p1:openSUSE:Factory/firewalld/14.0 p2:security:netfilter/firewalld/34.0
- devel c:security:netfilter/firewalld/34.0 p1:security:netfilter/firewalld/32.0
- factory c:openSUSE:Factory/firewalld/14.0 p1:openSUSE:Factory/firewalld/13.0 p2:security:netfilter/firewalld/32.0
- devel c:security:netfilter/firewalld/32.0 p1:security:netfilter/firewalld/30.0
- factory c:openSUSE:Factory/firewalld/13.0 p1:openSUSE:Factory/firewalld/12.0 p2:security:netfilter/firewalld/30.0
- devel c:security:netfilter/firewalld/30.0 p1:security:netfilter/firewalld/28.0
- factory c:openSUSE:Factory/firewalld/12.0 p1:openSUSE:Factory/firewalld/11.0 p2:security:netfilter/firewalld/28.0
- devel c:security:netfilter/firewalld/28.0 p1:security:netfilter/firewalld/26.0
- factory c:openSUSE:Factory/firewalld/11.0 p1:openSUSE:Factory/firewalld/10.0 p2:security:netfilter/firewalld/26.0
- devel c:security:netfilter/firewalld/26.0 p1:security:netfilter/firewalld/24.0
- factory c:openSUSE:Factory/firewalld/10.0 p1:openSUSE:Factory/firewalld/9.0 p2:security:netfilter/firewalld/24.0
- devel c:security:netfilter/firewalld/24.0 p1:security:netfilter/firewalld/22.0
- factory c:openSUSE:Factory/firewalld/9.0 p1:openSUSE:Factory/firewalld/8.0 p2:security:netfilter/firewalld/22.0
- devel c:security:netfilter/firewalld/22.0 p1:security:netfilter/firewalld/21.0
- devel c:security:netfilter/firewalld/21.0 p1:security:netfilter/firewalld/19.0
- factory c:openSUSE:Factory/firewalld/8.0 p1:openSUSE:Factory/firewalld/7.0 p2:security:netfilter/firewalld/19.0
- devel c:security:netfilter/firewalld/19.0 p1:security:netfilter/firewalld/17.0
- factory c:openSUSE:Factory/firewalld/7.0 p1:openSUSE:Factory/firewalld/6.0 p2:security:netfilter/firewalld/17.0
- devel c:security:netfilter/firewalld/17.0 p1:security:netfilter/firewalld/15.0
- factory c:openSUSE:Factory/firewalld/6.0 p1:openSUSE:Factory/firewalld/5.0 p2:security:netfilter/firewalld/15.0
- devel c:security:netfilter/firewalld/15.0 p1:security:netfilter/firewalld/13.0
- factory c:openSUSE:Factory/firewalld/5.0 p1:openSUSE:Factory/firewalld/4.0 p2:security:netfilter/firewalld/13.0
- devel c:security:netfilter/firewalld/13.0 p1:security:netfilter/firewalld/11.0
- factory c:openSUSE:Factory/firewalld/4.0 p1:openSUSE:Factory/firewalld/3.0 p2:security:netfilter/firewalld/11.0
- devel c:security:netfilter/firewalld/11.0 p1:security:netfilter/firewalld/9.0
- factory c:openSUSE:Factory/firewalld/3.0 p1:openSUSE:Factory/firewalld/2.0 p2:security:netfilter/firewalld/9.0
- devel c:security:netfilter/firewalld/9.0 p1:security:netfilter/firewalld/8.0
- devel c:security:netfilter/firewalld/8.0 p1:security:netfilter/firewalld/6.0
- factory c:openSUSE:Factory/firewalld/2.0 p1:openSUSE:Factory/firewalld/1.0 p2:security:netfilter/firewalld/6.0
- devel c:security:netfilter/firewalld/6.0 p1:security:netfilter/firewalld/5.0
- devel c:security:netfilter/firewalld/5.0 p1:security:netfilter/firewalld/4.0
- devel c:security:netfilter/firewalld/4.0 p1:openSUSE:Factory/firewalld/1.0
- factory c:openSUSE:Factory/firewalld/1.0 p1:security:netfilter/firewalld/2.0
- factory c:security:netfilter/firewalld/2.0 p1:security:netfilter/firewalld/1.0
- factory c:security:netfilter/firewalld/1.0

View File

@ -1,240 +0,0 @@
- commit: openSUSE:Factory/firewalld/73.0
merged:
- security:netfilter/firewalld/131.0
- commit: openSUSE:Factory/firewalld/72.0
merged:
- security:netfilter/firewalld/129.0
- security:netfilter/firewalld/128.0
- commit: openSUSE:Factory/firewalld/71.0
merged:
- security:netfilter/firewalld/127.0
- commit: openSUSE:Factory/firewalld/70.0
- commit: openSUSE:Factory/firewalld/69.0
merged:
- security:netfilter/firewalld/126.0
- commit: openSUSE:Factory/firewalld/68.0
merged:
- security:netfilter/firewalld/125.0
- commit: openSUSE:Factory/firewalld/67.0
merged:
- security:netfilter/firewalld/124.0
- commit: openSUSE:Factory/firewalld/66.0
merged:
- security:netfilter/firewalld/123.0
- commit: openSUSE:Factory/firewalld/65.0
merged:
- security:netfilter/firewalld/122.0
- commit: openSUSE:Factory/firewalld/64.0
merged:
- security:netfilter/firewalld/121.0
- commit: openSUSE:Factory/firewalld/63.0
merged:
- security:netfilter/firewalld/120.0
- commit: openSUSE:Factory/firewalld/62.0
merged:
- security:netfilter/firewalld/119.0
- commit: openSUSE:Factory/firewalld/61.0
merged:
- security:netfilter/firewalld/118.0
- commit: openSUSE:Factory/firewalld/60.0
merged:
- security:netfilter/firewalld/117.0
- commit: openSUSE:Factory/firewalld/59.0
merged:
- security:netfilter/firewalld/116.0
- commit: openSUSE:Factory/firewalld/58.0
merged:
- security:netfilter/firewalld/115.0
- commit: openSUSE:Factory/firewalld/57.0
merged:
- security:netfilter/firewalld/114.0
- commit: openSUSE:Factory/firewalld/56.0
merged:
- security:netfilter/firewalld/113.0
- commit: openSUSE:Factory/firewalld/55.0
merged:
- security:netfilter/firewalld/112.0
- security:netfilter/firewalld/111.0
- security:netfilter/firewalld/110.0
- security:netfilter/firewalld/109.0
- commit: openSUSE:Factory/firewalld/54.0
merged:
- security:netfilter/firewalld/108.0
- commit: openSUSE:Factory/firewalld/53.0
merged:
- security:netfilter/firewalld/107.0
- commit: openSUSE:Factory/firewalld/52.0
- commit: openSUSE:Factory/firewalld/51.0
- commit: openSUSE:Factory/firewalld/50.0
merged:
- security:netfilter/firewalld/106.0
- commit: openSUSE:Factory/firewalld/49.0
merged:
- security:netfilter/firewalld/105.0
- security:netfilter/firewalld/104.0
- security:netfilter/firewalld/103.0
- commit: openSUSE:Factory/firewalld/48.0
merged:
- security:netfilter/firewalld/102.0
- commit: openSUSE:Factory/firewalld/47.0
merged:
- security:netfilter/firewalld/101.0
- commit: openSUSE:Factory/firewalld/46.0
merged:
- security:netfilter/firewalld/100.0
- commit: openSUSE:Factory/firewalld/45.0
merged:
- security:netfilter/firewalld/99.0
- commit: openSUSE:Factory/firewalld/44.0
merged:
- security:netfilter/firewalld/98.0
- commit: openSUSE:Factory/firewalld/43.0
merged:
- security:netfilter/firewalld/97.0
- security:netfilter/firewalld/96.0
- security:netfilter/firewalld/95.0
- security:netfilter/firewalld/94.0
- commit: openSUSE:Factory/firewalld/42.0
merged:
- security:netfilter/firewalld/93.0
- commit: openSUSE:Factory/firewalld/41.0
merged:
- security:netfilter/firewalld/92.0
- commit: openSUSE:Factory/firewalld/40.0
merged:
- security:netfilter/firewalld/91.0
- commit: openSUSE:Factory/firewalld/39.0
merged:
- security:netfilter/firewalld/90.0
- commit: openSUSE:Factory/firewalld/38.0
merged:
- security:netfilter/firewalld/89.0
- commit: openSUSE:Factory/firewalld/37.0
merged:
- security:netfilter/firewalld/88.0
- security:netfilter/firewalld/87.0
- security:netfilter/firewalld/86.0
- security:netfilter/firewalld/85.0
- commit: openSUSE:Factory/firewalld/36.0
merged:
- security:netfilter/firewalld/84.0
- security:netfilter/firewalld/83.0
- commit: openSUSE:Factory/firewalld/35.0
merged:
- security:netfilter/firewalld/82.0
- security:netfilter/firewalld/81.0
- security:netfilter/firewalld/80.0
- security:netfilter/firewalld/79.0
- security:netfilter/firewalld/78.0
- commit: openSUSE:Factory/firewalld/34.0
merged:
- security:netfilter/firewalld/77.0
- security:netfilter/firewalld/76.0
- security:netfilter/firewalld/75.0
- commit: openSUSE:Factory/firewalld/33.0
- commit: openSUSE:Factory/firewalld/32.0
merged:
- security:netfilter/firewalld/74.0
- commit: openSUSE:Factory/firewalld/31.0
- commit: openSUSE:Factory/firewalld/30.0
merged:
- security:netfilter/firewalld/71.0
- commit: openSUSE:Factory/firewalld/29.0
merged:
- security:netfilter/firewalld/69.0
- commit: openSUSE:Factory/firewalld/28.0
merged:
- security:netfilter/firewalld/68.0
- security:netfilter/firewalld/67.0
- commit: openSUSE:Factory/firewalld/27.0
merged:
- security:netfilter/firewalld/65.0
- commit: openSUSE:Factory/firewalld/26.0
merged:
- security:netfilter/firewalld/63.0
- commit: openSUSE:Factory/firewalld/25.0
merged:
- security:netfilter/firewalld/61.0
- security:netfilter/firewalld/60.0
- security:netfilter/firewalld/59.0
- commit: openSUSE:Factory/firewalld/24.0
merged:
- security:netfilter/firewalld/57.0
- commit: openSUSE:Factory/firewalld/23.0
merged:
- security:netfilter/firewalld/55.0
- security:netfilter/firewalld/54.0
- security:netfilter/firewalld/53.0
- commit: openSUSE:Factory/firewalld/22.0
merged:
- security:netfilter/firewalld/51.0
- security:netfilter/firewalld/50.0
- commit: openSUSE:Factory/firewalld/21.0
merged:
- security:netfilter/firewalld/48.0
- security:netfilter/firewalld/47.0
- commit: openSUSE:Factory/firewalld/20.0
merged:
- security:netfilter/firewalld/45.0
- commit: openSUSE:Factory/firewalld/19.0
merged:
- security:netfilter/firewalld/43.0
- commit: openSUSE:Factory/firewalld/18.0
merged:
- security:netfilter/firewalld/41.0
- commit: openSUSE:Factory/firewalld/17.0
merged:
- security:netfilter/firewalld/39.0
- security:netfilter/firewalld/38.0
- commit: openSUSE:Factory/firewalld/16.0
merged:
- security:netfilter/firewalld/36.0
- commit: openSUSE:Factory/firewalld/15.0
merged:
- security:netfilter/firewalld/34.0
- commit: openSUSE:Factory/firewalld/14.0
merged:
- security:netfilter/firewalld/32.0
- commit: openSUSE:Factory/firewalld/13.0
merged:
- security:netfilter/firewalld/30.0
- commit: openSUSE:Factory/firewalld/12.0
merged:
- security:netfilter/firewalld/28.0
- commit: openSUSE:Factory/firewalld/11.0
merged:
- security:netfilter/firewalld/26.0
- commit: openSUSE:Factory/firewalld/10.0
merged:
- security:netfilter/firewalld/24.0
- commit: openSUSE:Factory/firewalld/9.0
merged:
- security:netfilter/firewalld/22.0
- security:netfilter/firewalld/21.0
- commit: openSUSE:Factory/firewalld/8.0
merged:
- security:netfilter/firewalld/19.0
- commit: openSUSE:Factory/firewalld/7.0
merged:
- security:netfilter/firewalld/17.0
- commit: openSUSE:Factory/firewalld/6.0
merged:
- security:netfilter/firewalld/15.0
- commit: openSUSE:Factory/firewalld/5.0
merged:
- security:netfilter/firewalld/13.0
- commit: openSUSE:Factory/firewalld/4.0
merged:
- security:netfilter/firewalld/11.0
- commit: openSUSE:Factory/firewalld/3.0
merged:
- security:netfilter/firewalld/9.0
- security:netfilter/firewalld/8.0
- commit: openSUSE:Factory/firewalld/2.0
merged:
- security:netfilter/firewalld/6.0
- security:netfilter/firewalld/5.0
- security:netfilter/firewalld/4.0
- commit: openSUSE:Factory/firewalld/1.0
- commit: security:netfilter/firewalld/2.0
- commit: security:netfilter/firewalld/1.0

View File

@ -65,15 +65,6 @@ class TestTreeMethods(unittest.TestCase):
def test_000update_repos_tree(self):
self.verify_package("000update-repos")
def test_breeze_tree(self):
self.verify_package("breeze")
def test_firewalld_tree(self):
self.verify_package("firewalld")
def test_FastCGI_tree(self):
self.verify_package("FastCGI")
if __name__ == "__main__":
unittest.main()

View File

@ -1,19 +0,0 @@
#!/bin/bash
#
cd /space/dmueller/git-importer
source credentials.sh
while true; do
for i in $PWD/tasks/*; do
if test -f "$i"; then
echo "$(date): Importing $(basename $i)"
if ! python3 ./git-importer.py -c repos/.cache $(basename $i); then
mkdir -p $PWD/failed-tasks
mv -f $i $PWD/failed-tasks
fi
rm -f $i
fi
done
inotifywait -q -e create $PWD/tasks
done