|
Benjamin Poirier |
600ead |
#!/usr/bin/python3
|
|
Benjamin Poirier |
0aaea3 |
# -*- coding: utf-8 -*-
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
0aaea3 |
import argparse
|
|
Benjamin Poirier |
0abe03 |
import bisect
|
|
Benjamin Poirier |
897bbc |
import collections
|
|
Benjamin Poirier |
600ead |
import dbm
|
|
Benjamin Poirier |
404509 |
import functools
|
|
Benjamin Poirier |
e8d72d |
import operator
|
|
Benjamin Poirier |
0aaea3 |
import os
|
|
Benjamin Poirier |
0aaea3 |
import os.path
|
|
Benjamin Poirier |
0aaea3 |
import pprint
|
|
Benjamin Poirier |
0aaea3 |
import pygit2
|
|
Benjamin Poirier |
897bbc |
import re
|
|
Benjamin Poirier |
0aaea3 |
import shelve
|
|
Benjamin Poirier |
0aaea3 |
import subprocess
|
|
Benjamin Poirier |
0aaea3 |
import sys
|
|
Benjamin Poirier |
587a8d |
import types
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
e8de5e |
class GSException(BaseException):
|
|
Benjamin Poirier |
e8de5e |
pass
|
|
Benjamin Poirier |
e8de5e |
|
|
Benjamin Poirier |
e8de5e |
|
|
Benjamin Poirier |
e8de5e |
class GSError(GSException):
|
|
Benjamin Poirier |
e8de5e |
pass
|
|
Benjamin Poirier |
e8de5e |
|
|
Benjamin Poirier |
e8de5e |
|
|
Benjamin Poirier |
544f5d |
class GSKeyError(GSException):
|
|
Benjamin Poirier |
544f5d |
pass
|
|
Benjamin Poirier |
544f5d |
|
|
Benjamin Poirier |
544f5d |
|
|
Benjamin Poirier |
0abe03 |
class GSNotFound(GSException):
|
|
Benjamin Poirier |
0abe03 |
pass
|
|
Benjamin Poirier |
0abe03 |
|
|
Benjamin Poirier |
0abe03 |
|
|
Benjamin Poirier |
897bbc |
class RepoURL(object):
|
|
Benjamin Poirier |
897bbc |
k_org_canon_prefix = "git://git.kernel.org/pub/scm/linux/kernel/git/"
|
|
Benjamin Poirier |
897bbc |
proto_match = re.compile("(git|https?)://")
|
|
Benjamin Poirier |
897bbc |
ext = ".git"
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
897bbc |
def __init__(self, url):
|
|
Benjamin Poirier |
2056d7 |
if url is None or url == repr(None):
|
|
Benjamin Poirier |
2056d7 |
self.url = None
|
|
Benjamin Poirier |
2056d7 |
return
|
|
Benjamin Poirier |
2056d7 |
|
|
Benjamin Poirier |
897bbc |
k_org_prefixes = [
|
|
Benjamin Poirier |
720a01 |
"http://git.kernel.org/pub/scm/linux/kernel/git/",
|
|
Benjamin Poirier |
897bbc |
"https://git.kernel.org/pub/scm/linux/kernel/git/",
|
|
Benjamin Poirier |
897bbc |
"https://kernel.googlesource.com/pub/scm/linux/kernel/git/",
|
|
Benjamin Poirier |
897bbc |
]
|
|
Benjamin Poirier |
897bbc |
for prefix in k_org_prefixes:
|
|
Benjamin Poirier |
897bbc |
if url.startswith(prefix):
|
|
Benjamin Poirier |
897bbc |
url = url.replace(prefix, self.k_org_canon_prefix)
|
|
Benjamin Poirier |
897bbc |
break
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
16928c |
if not self.proto_match.match(url):
|
|
Benjamin Poirier |
897bbc |
url = self.k_org_canon_prefix + url
|
|
Benjamin Poirier |
439e8e |
|
|
Benjamin Poirier |
439e8e |
if not url.endswith(self.ext):
|
|
Benjamin Poirier |
439e8e |
url = url + self.ext
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
5aa06b |
# an undocumented alias
|
|
Benjamin Poirier |
5aa06b |
if url == "git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git":
|
|
Benjamin Poirier |
5aa06b |
url = "git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git"
|
|
Benjamin Poirier |
5aa06b |
|
|
Benjamin Poirier |
897bbc |
self.url = url
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
404509 |
def _is_valid_operand(self, other):
|
|
Benjamin Poirier |
404509 |
return hasattr(other, "url")
|
|
Benjamin Poirier |
404509 |
|
|
Benjamin Poirier |
404509 |
|
|
Benjamin Poirier |
897bbc |
def __eq__(self, other):
|
|
Benjamin Poirier |
404509 |
if not self._is_valid_operand(other):
|
|
Benjamin Poirier |
404509 |
return NotImplemented
|
|
Benjamin Poirier |
897bbc |
return self.url == other.url
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
404509 |
def __ne__(self, other):
|
|
Benjamin Poirier |
404509 |
if not self._is_valid_operand(other):
|
|
Benjamin Poirier |
404509 |
return NotImplemented
|
|
Benjamin Poirier |
404509 |
return self.url != other.url
|
|
Benjamin Poirier |
7b51a6 |
|
|
Benjamin Poirier |
7b51a6 |
|
|
Benjamin Poirier |
897bbc |
def __hash__(self):
|
|
Benjamin Poirier |
897bbc |
return hash(self.url)
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
def __repr__(self):
|
|
Benjamin Poirier |
897bbc |
return "%s" % (self.url,)
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
def __str__(self):
|
|
Benjamin Poirier |
897bbc |
url = self.url
|
|
Benjamin Poirier |
2056d7 |
if url is None:
|
|
Benjamin Poirier |
897bbc |
url = ""
|
|
Benjamin Poirier |
2056d7 |
elif url.startswith(self.k_org_canon_prefix) and url.endswith(self.ext):
|
|
Benjamin Poirier |
2056d7 |
url = url[len(self.k_org_canon_prefix):-1 * len(self.ext)]
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
return url
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
404509 |
@functools.total_ordering
|
|
Benjamin Poirier |
897bbc |
class Head(object):
|
|
Benjamin Poirier |
897bbc |
def __init__(self, repo_url, rev="master"):
|
|
Benjamin Poirier |
897bbc |
self.repo_url = repo_url
|
|
Benjamin Poirier |
897bbc |
self.rev = rev
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
404509 |
def _is_valid_operand(self, other):
|
|
Benjamin Poirier |
404509 |
return hasattr(other, "repo_url") and hasattr(other, "rev")
|
|
Benjamin Poirier |
404509 |
|
|
Benjamin Poirier |
404509 |
|
|
Benjamin Poirier |
404509 |
def _get_index(self):
|
|
Benjamin Poirier |
404509 |
"""
|
|
Benjamin Poirier |
404509 |
A head with no url is considered out of tree. Any other head with a
|
|
Benjamin Poirier |
404509 |
url is upstream of it.
|
|
Benjamin Poirier |
404509 |
"""
|
|
Benjamin Poirier |
404509 |
if self.repo_url == RepoURL(None):
|
|
Benjamin Poirier |
404509 |
return len(remotes)
|
|
Benjamin Poirier |
404509 |
else:
|
|
Benjamin Poirier |
404509 |
return remote_index[self]
|
|
Benjamin Poirier |
404509 |
|
|
Benjamin Poirier |
404509 |
|
|
Benjamin Poirier |
897bbc |
def __eq__(self, other):
|
|
Benjamin Poirier |
404509 |
if not self._is_valid_operand(other):
|
|
Benjamin Poirier |
404509 |
return NotImplemented
|
|
Benjamin Poirier |
897bbc |
return (self.repo_url == other.repo_url and self.rev == other.rev)
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
404509 |
def __lt__(self, other):
|
|
Benjamin Poirier |
404509 |
if not self._is_valid_operand(other):
|
|
Benjamin Poirier |
404509 |
return NotImplemented
|
|
Benjamin Poirier |
404509 |
return self._get_index() < other._get_index()
|
|
Benjamin Poirier |
404509 |
|
|
Benjamin Poirier |
404509 |
|
|
Benjamin Poirier |
897bbc |
def __hash__(self):
|
|
Benjamin Poirier |
897bbc |
return hash((self.repo_url, self.rev,))
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
76bf0c |
def __repr__(self):
|
|
Benjamin Poirier |
897bbc |
return "%s %s" % (repr(self.repo_url), self.rev,)
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
def __str__(self):
|
|
Benjamin Poirier |
897bbc |
url = str(self.repo_url)
|
|
Benjamin Poirier |
897bbc |
if self.rev == "master":
|
|
Benjamin Poirier |
897bbc |
return url
|
|
Benjamin Poirier |
897bbc |
else:
|
|
Benjamin Poirier |
897bbc |
result = "%s %s" % (url, self.rev,)
|
|
Benjamin Poirier |
897bbc |
return result.strip()
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
# a list of each remote head which is indexed by this script
|
|
Benjamin Poirier |
7c5bc6 |
# If the working repository is a clone of linux.git (it fetches from mainline,
|
|
Benjamin Poirier |
7c5bc6 |
# the first remote) and a commit does not appear in one of these remotes, it is
|
|
Benjamin Poirier |
7c5bc6 |
# considered "not upstream" and cannot be sorted.
|
|
Benjamin Poirier |
897bbc |
# Repositories that come first in the list should be pulling/merging from
|
|
Benjamin Poirier |
897bbc |
# repositories lower down in the list. Said differently, commits should trickle
|
|
Benjamin Poirier |
897bbc |
# up from repositories at the end of the list to repositories higher up. For
|
|
Benjamin Poirier |
897bbc |
# example, network commits usually follow "net-next" -> "net" -> "linux.git".
|
|
Benjamin Poirier |
897bbc |
#
|
|
Benjamin Poirier |
4809eb |
# linux-next is not a good reference because it gets rebased. If a commit is in
|
|
Benjamin Poirier |
4809eb |
# linux-next, it comes from some other tree. Please tag the patch accordingly.
|
|
Benjamin Poirier |
4809eb |
#
|
|
Benjamin Poirier |
897bbc |
# Head(RepoURL(remote url), remote branch name)[]
|
|
Benjamin Poirier |
897bbc |
# Note that "remote url" can be abbreviated if it starts with one of the usual
|
|
Benjamin Poirier |
897bbc |
# kernel.org prefixes and "remote branch name" can be omitted if it is "master".
|
|
Benjamin Poirier |
897bbc |
remotes = (
|
|
Benjamin Poirier |
897bbc |
Head(RepoURL("torvalds/linux.git")),
|
|
Benjamin Poirier |
897bbc |
Head(RepoURL("davem/net.git")),
|
|
Benjamin Poirier |
897bbc |
Head(RepoURL("davem/net-next.git")),
|
|
Michal Suchanek |
ec2ea2 |
Head(RepoURL("rdma/rdma.git"), "for-rc"),
|
|
Michal Suchanek |
ec2ea2 |
Head(RepoURL("rdma/rdma.git"), "for-next"),
|
|
Benjamin Poirier |
897bbc |
Head(RepoURL("dledford/rdma.git"), "k.o/for-next"),
|
|
Benjamin Poirier |
897bbc |
Head(RepoURL("jejb/scsi.git"), "for-next"),
|
|
Benjamin Poirier |
897bbc |
Head(RepoURL("bp/bp.git"), "for-next"),
|
|
Benjamin Poirier |
897bbc |
Head(RepoURL("tiwai/sound.git")),
|
|
Benjamin Poirier |
131a90 |
Head(RepoURL("git://linuxtv.org/media_tree.git")),
|
|
Benjamin Poirier |
897bbc |
Head(RepoURL("powerpc/linux.git"), "next"),
|
|
Jeff Mahoney |
1f975c |
Head(RepoURL("powerpc/linux.git"), "fixes"),
|
|
Benjamin Poirier |
897bbc |
Head(RepoURL("tip/tip.git")),
|
|
Benjamin Poirier |
5d61b5 |
Head(RepoURL("shli/md.git"), "for-next"),
|
|
Benjamin Poirier |
5d61b5 |
Head(RepoURL("dhowells/linux-fs.git"), "keys-uefi"),
|
|
Jiri Kosina |
9f5c18 |
Head(RepoURL("tytso/ext4.git"), "dev"),
|
|
Hannes Reinecke |
58f8a7 |
Head(RepoURL("s390/linux.git"), "for-linus"),
|
|
Benjamin Poirier |
a3e6d3 |
Head(RepoURL("tj/libata.git"), "for-next"),
|
|
Benjamin Poirier |
131a90 |
Head(RepoURL("https://github.com/kdave/btrfs-devel.git"), "misc-next"),
|
|
Benjamin Poirier |
a3e6d3 |
Head(RepoURL("git://people.freedesktop.org/~airlied/linux"), "drm-next"),
|
|
Benjamin Poirier |
a3e6d3 |
Head(RepoURL("gregkh/tty.git"), "tty-next"),
|
|
Goldwyn Rodrigues |
d8d320 |
Head(RepoURL("jj/linux-apparmor.git"), "apparmor-next"),
|
|
Michal Kubecek |
daa89d |
Head(RepoURL("pablo/nf.git")),
|
|
Michal Kubecek |
daa89d |
Head(RepoURL("pablo/nf-next.git")),
|
|
Michal Kubecek |
daa89d |
Head(RepoURL("horms/ipvs.git")),
|
|
Michal Kubecek |
daa89d |
Head(RepoURL("horms/ipvs-next.git")),
|
|
Michal Kubecek |
daa89d |
Head(RepoURL("klassert/ipsec.git")),
|
|
Michal Kubecek |
daa89d |
Head(RepoURL("klassert/ipsec-next.git")),
|
|
Hannes Reinecke |
9ef036 |
Head(RepoURL("mkp/scsi.git"), "4.19/scsi-queue"),
|
|
Borislav Petkov |
9adbb9 |
Head(RepoURL("git://git.kernel.dk/linux-block.git"), "for-next"),
|
|
Johannes Thumshirn |
40fc9c |
Head(RepoURL("git://git.kernel.org/pub/scm/virt/kvm/kvm.git"), "queue"),
|
|
Hannes Reinecke |
9c28cf |
Head(RepoURL("git://git.infradead.org/nvme.git"), "nvme-4.18"),
|
|
Hannes Reinecke |
c95650 |
Head(RepoURL("git://git.infradead.org/nvme.git"), "nvme-4.19"),
|
|
Michal Suchanek |
f433a0 |
Head(RepoURL("dhowells/linux-fs.git")),
|
|
Benjamin Poirier |
64ee72 |
Head(RepoURL("herbert/cryptodev-2.6.git")),
|
|
Olaf Hering |
718b01 |
Head(RepoURL("helgaas/pci.git"), "next"),
|
|
Luis Henriques |
82df85 |
Head(RepoURL("viro/vfs.git"), "for-linus"),
|
|
Jessica Yu |
31d0a4 |
Head(RepoURL("jeyu/linux.git"), "modules-next"),
|
|
Joerg Roedel |
d89aa8 |
Head(RepoURL("joro/iommu.git"), "next"),
|
|
Benjamin Poirier |
897bbc |
)
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
600ead |
remote_index = dict(zip(remotes, list(range(len(remotes)))))
|
|
Benjamin Poirier |
1a4488 |
oot = Head(RepoURL(None), "out-of-tree patches")
|
|
Benjamin Poirier |
8d50e8 |
|
|
Benjamin Poirier |
587a8d |
remote_match = re.compile("remote\..+\.url")
|
|
Benjamin Poirier |
8d50e8 |
|
|
Benjamin Poirier |
4f1bbb |
|
|
Benjamin Poirier |
f17031 |
def config_keys(repo):
|
|
Benjamin Poirier |
f17031 |
"""
|
|
Benjamin Poirier |
f17031 |
With libgit < 0.27, pygit2's Config.__iter__() elements are str.
|
|
Benjamin Poirier |
f17031 |
With libgit 0.27, the same elements are ConfigEntry instances.
|
|
Benjamin Poirier |
f17031 |
|
|
Benjamin Poirier |
f17031 |
This function is an adaptation layer to support both interfaces.
|
|
Benjamin Poirier |
f17031 |
"""
|
|
Benjamin Poirier |
f17031 |
try:
|
|
Benjamin Poirier |
f17031 |
first = repo.config.__iter__().next()
|
|
Benjamin Poirier |
f17031 |
except StopIteration:
|
|
Benjamin Poirier |
f17031 |
return
|
|
Benjamin Poirier |
f17031 |
|
|
Benjamin Poirier |
f17031 |
if isinstance(first, pygit2.config.ConfigEntry):
|
|
Benjamin Poirier |
f17031 |
transform = lambda config_entry: config_entry.name
|
|
Benjamin Poirier |
f17031 |
else:
|
|
Benjamin Poirier |
f17031 |
transform = lambda name: name
|
|
Benjamin Poirier |
f17031 |
|
|
Benjamin Poirier |
f17031 |
for entry in repo.config:
|
|
Benjamin Poirier |
f17031 |
yield transform(entry)
|
|
Benjamin Poirier |
f17031 |
|
|
Benjamin Poirier |
f17031 |
|
|
Benjamin Poirier |
587a8d |
def get_heads(repo):
|
|
Benjamin Poirier |
587a8d |
"""
|
|
Benjamin Poirier |
587a8d |
Returns
|
|
Benjamin Poirier |
587a8d |
repo_heads[Head]
|
|
Benjamin Poirier |
587a8d |
sha1
|
|
Benjamin Poirier |
587a8d |
"""
|
|
Benjamin Poirier |
587a8d |
result = collections.OrderedDict()
|
|
Benjamin Poirier |
587a8d |
repo_remotes = collections.OrderedDict([
|
|
Benjamin Poirier |
587a8d |
(RepoURL(repo.config[name]), ".".join(name.split(".")[1:-1]))
|
|
Benjamin Poirier |
f17031 |
for name in config_keys(repo)
|
|
Benjamin Poirier |
587a8d |
if remote_match.match(name)])
|
|
Benjamin Poirier |
4f1bbb |
|
|
Benjamin Poirier |
587a8d |
for head in remotes:
|
|
Benjamin Poirier |
7c5bc6 |
if head in result:
|
|
Benjamin Poirier |
7c5bc6 |
raise GSException("head \"%s\" is not unique." % (head,))
|
|
Benjamin Poirier |
7c5bc6 |
|
|
Benjamin Poirier |
76bf0c |
try:
|
|
Benjamin Poirier |
587a8d |
remote_name = repo_remotes[head.repo_url]
|
|
Benjamin Poirier |
587a8d |
except KeyError:
|
|
Benjamin Poirier |
587a8d |
continue
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
587a8d |
rev = "remotes/%s/%s" % (remote_name, head.rev,)
|
|
Benjamin Poirier |
587a8d |
try:
|
|
Benjamin Poirier |
587a8d |
commit = repo.revparse_single(rev)
|
|
Benjamin Poirier |
587a8d |
except KeyError:
|
|
Benjamin Poirier |
587a8d |
raise GSError(
|
|
Benjamin Poirier |
587a8d |
"Could not read revision \"%s\". Perhaps you need to "
|
|
Benjamin Poirier |
587a8d |
"fetch from remote \"%s\", ie. `git fetch %s`." % (
|
|
Benjamin Poirier |
587a8d |
rev, remote_name, remote_name,))
|
|
Benjamin Poirier |
587a8d |
result[head] = str(commit.id)
|
|
Benjamin Poirier |
587a8d |
|
|
Benjamin Poirier |
600ead |
if len(result) == 0 or list(result.keys())[0] != remotes[0]:
|
|
Benjamin Poirier |
587a8d |
# According to the urls in remotes, this is not a clone of linux.git
|
|
Benjamin Poirier |
587a8d |
# Sort according to commits reachable from the current head
|
|
Benjamin Poirier |
587a8d |
result = collections.OrderedDict(
|
|
Benjamin Poirier |
587a8d |
[(Head(RepoURL(None), "HEAD"),
|
|
Benjamin Poirier |
587a8d |
str(repo.revparse_single("HEAD").id),)])
|
|
Benjamin Poirier |
587a8d |
|
|
Benjamin Poirier |
587a8d |
return result
|
|
Benjamin Poirier |
587a8d |
|
|
Benjamin Poirier |
587a8d |
|
|
Benjamin Poirier |
587a8d |
def get_history(repo, repo_heads):
|
|
Benjamin Poirier |
587a8d |
"""
|
|
Benjamin Poirier |
587a8d |
Returns
|
|
Benjamin Poirier |
587a8d |
history[Head][commit hash represented as string of 40 characters]
|
|
Benjamin Poirier |
587a8d |
index, an ordinal number such that
|
|
Benjamin Poirier |
587a8d |
commit a is an ancestor of commit b -> index(a) < index(b)
|
|
Benjamin Poirier |
587a8d |
"""
|
|
Benjamin Poirier |
587a8d |
processed = []
|
|
Benjamin Poirier |
587a8d |
history = collections.OrderedDict()
|
|
Benjamin Poirier |
587a8d |
args = ["git", "log", "--topo-order", "--pretty=tformat:%H"]
|
|
Benjamin Poirier |
587a8d |
for head, rev in repo_heads.items():
|
|
Benjamin Poirier |
587a8d |
sp = subprocess.Popen(args + processed + [rev],
|
|
Benjamin Poirier |
587a8d |
cwd=repo.path,
|
|
Benjamin Poirier |
587a8d |
env={},
|
|
Benjamin Poirier |
587a8d |
stdout=subprocess.PIPE,
|
|
Benjamin Poirier |
587a8d |
stderr=subprocess.STDOUT)
|
|
Benjamin Poirier |
587a8d |
|
|
Benjamin Poirier |
587a8d |
result = {}
|
|
Benjamin Poirier |
587a8d |
for l in sp.stdout:
|
|
Benjamin Poirier |
600ead |
result[l.decode().strip()] = len(result)
|
|
Benjamin Poirier |
587a8d |
# reverse indexes
|
|
Benjamin Poirier |
587a8d |
history[head] = {commit : len(result) - val for commit, val in
|
|
Benjamin Poirier |
587a8d |
result.items()}
|
|
Benjamin Poirier |
587a8d |
|
|
Benjamin Poirier |
587a8d |
sp.communicate()
|
|
Benjamin Poirier |
587a8d |
if sp.returncode != 0:
|
|
Benjamin Poirier |
587a8d |
raise GSError("git log exited with an error:\n" +
|
|
Benjamin Poirier |
587a8d |
"\n".join(history[head]))
|
|
Benjamin Poirier |
587a8d |
|
|
Benjamin Poirier |
587a8d |
processed.append("^%s" % (rev,))
|
|
Benjamin Poirier |
587a8d |
|
|
Benjamin Poirier |
587a8d |
return history
|
|
Benjamin Poirier |
587a8d |
|
|
Benjamin Poirier |
8d50e8 |
|
|
Benjamin Poirier |
2c7d8e |
class CException(BaseException):
|
|
Benjamin Poirier |
2c7d8e |
pass
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
2c7d8e |
class CError(CException):
|
|
Benjamin Poirier |
2c7d8e |
pass
|
|
Benjamin Poirier |
f28d0a |
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
2c7d8e |
class CNeedsRebuild(CException):
|
|
Benjamin Poirier |
2c7d8e |
pass
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
2c7d8e |
class CAbsent(CNeedsRebuild):
|
|
Benjamin Poirier |
2c7d8e |
pass
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
2c7d8e |
class CKeyError(CNeedsRebuild):
|
|
Benjamin Poirier |
2c7d8e |
pass
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
2c7d8e |
class CUnsupported(CNeedsRebuild):
|
|
Benjamin Poirier |
2c7d8e |
pass
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
2c7d8e |
class CInconsistent(CNeedsRebuild):
|
|
Benjamin Poirier |
2c7d8e |
pass
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
class Cache(object):
|
|
Benjamin Poirier |
2c7d8e |
"""
|
|
Benjamin Poirier |
2c7d8e |
cache
|
|
Benjamin Poirier |
2c7d8e |
version
|
|
Benjamin Poirier |
2c7d8e |
history[]
|
|
Benjamin Poirier |
2c7d8e |
(url, rev, sha1,
|
|
Benjamin Poirier |
2c7d8e |
history[commit hash represented as string of 40 characters]
|
|
Benjamin Poirier |
2c7d8e |
index (as described in get_history())
|
|
Benjamin Poirier |
2c7d8e |
,)
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
The cache is stored using basic types.
|
|
Benjamin Poirier |
2c7d8e |
"""
|
|
Benjamin Poirier |
2c7d8e |
version = 3
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
def __init__(self, write_enable=False):
|
|
Benjamin Poirier |
2c7d8e |
self.write_enable = write_enable
|
|
Benjamin Poirier |
2c7d8e |
self.closed = True
|
|
Benjamin Poirier |
db0e54 |
try:
|
|
Benjamin Poirier |
db0e54 |
cache_dir = os.environ["XDG_CACHE_HOME"]
|
|
Benjamin Poirier |
db0e54 |
except KeyError:
|
|
Benjamin Poirier |
db0e54 |
cache_dir = os.path.expanduser("~/.cache")
|
|
Benjamin Poirier |
2c7d8e |
cache_path = os.path.join(cache_dir, "git-sort")
|
|
Benjamin Poirier |
2c7d8e |
try:
|
|
Benjamin Poirier |
2c7d8e |
os.stat(cache_path)
|
|
Benjamin Poirier |
2c7d8e |
except OSError as e:
|
|
Benjamin Poirier |
2c7d8e |
if e.errno == 2:
|
|
Benjamin Poirier |
2c7d8e |
if write_enable:
|
|
Benjamin Poirier |
2c7d8e |
if not os.path.isdir(cache_dir):
|
|
Benjamin Poirier |
2c7d8e |
try:
|
|
Benjamin Poirier |
2c7d8e |
os.makedirs(cache_dir)
|
|
Benjamin Poirier |
2c7d8e |
except OSError as err:
|
|
Benjamin Poirier |
2c7d8e |
raise CError("Could not create cache directory:\n" +
|
|
Benjamin Poirier |
2c7d8e |
str(err))
|
|
Benjamin Poirier |
2c7d8e |
else:
|
|
Benjamin Poirier |
2c7d8e |
raise CAbsent
|
|
Benjamin Poirier |
2c7d8e |
else:
|
|
Benjamin Poirier |
2c7d8e |
raise
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
15bd1c |
if write_enable:
|
|
Benjamin Poirier |
15bd1c |
# In case there is already a database file of an unsupported format,
|
|
Benjamin Poirier |
15bd1c |
# one would hope that with flag="n" a new database would be created
|
|
Benjamin Poirier |
15bd1c |
# to overwrite the current one. Alas, that is not the case... :'(
|
|
Benjamin Poirier |
15bd1c |
try:
|
|
Benjamin Poirier |
15bd1c |
os.unlink(cache_path)
|
|
Benjamin Poirier |
15bd1c |
except OSError as e:
|
|
Benjamin Poirier |
15bd1c |
if e.errno != 2:
|
|
Benjamin Poirier |
15bd1c |
raise
|
|
Benjamin Poirier |
15bd1c |
|
|
Benjamin Poirier |
2c7d8e |
flag_map = {False : "r", True : "n"}
|
|
Benjamin Poirier |
15bd1c |
try:
|
|
Benjamin Poirier |
15bd1c |
self.cache = shelve.open(cache_path, flag=flag_map[write_enable])
|
|
Benjamin Poirier |
600ead |
except dbm.error:
|
|
Benjamin Poirier |
600ead |
raise CUnsupported
|
|
Benjamin Poirier |
2c7d8e |
self.closed = False
|
|
Benjamin Poirier |
2c7d8e |
if write_enable:
|
|
Benjamin Poirier |
2c7d8e |
self.cache["version"] = Cache.version
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
def __del__(self):
|
|
Benjamin Poirier |
2c7d8e |
self.close()
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
def __enter__(self):
|
|
Benjamin Poirier |
2c7d8e |
return self
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
def __exit__(self, *args):
|
|
Benjamin Poirier |
2c7d8e |
self.close()
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
def close(self):
|
|
Benjamin Poirier |
2c7d8e |
if not self.closed:
|
|
Benjamin Poirier |
2c7d8e |
self.cache.close()
|
|
Benjamin Poirier |
2c7d8e |
self.closed = True
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
def __getitem__(self, key):
|
|
Benjamin Poirier |
2c7d8e |
"""
|
|
Benjamin Poirier |
2c7d8e |
Supported keys:
|
|
Benjamin Poirier |
2c7d8e |
"version"
|
|
Benjamin Poirier |
2c7d8e |
int
|
|
Benjamin Poirier |
2c7d8e |
"history"
|
|
Benjamin Poirier |
2c7d8e |
OrderedDict((Head, sha1) : history)
|
|
Benjamin Poirier |
2c7d8e |
"""
|
|
Benjamin Poirier |
2c7d8e |
if self.closed:
|
|
Benjamin Poirier |
2c7d8e |
raise ValueError
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
15bd1c |
try:
|
|
Benjamin Poirier |
15bd1c |
version = self.cache["version"]
|
|
Benjamin Poirier |
15bd1c |
except KeyError:
|
|
Benjamin Poirier |
15bd1c |
key_error = True
|
|
Benjamin Poirier |
15bd1c |
except ValueError as err:
|
|
Benjamin Poirier |
15bd1c |
raise CUnsupported(str(err))
|
|
Benjamin Poirier |
15bd1c |
else:
|
|
Benjamin Poirier |
15bd1c |
key_error = False
|
|
Benjamin Poirier |
15bd1c |
|
|
Benjamin Poirier |
2c7d8e |
if key == "version":
|
|
Benjamin Poirier |
15bd1c |
if key_error:
|
|
Benjamin Poirier |
2c7d8e |
raise CKeyError
|
|
Benjamin Poirier |
15bd1c |
else:
|
|
Benjamin Poirier |
15bd1c |
return version
|
|
Benjamin Poirier |
2c7d8e |
elif key == "history":
|
|
Benjamin Poirier |
15bd1c |
if key_error or version != Cache.version:
|
|
Benjamin Poirier |
2c7d8e |
raise CUnsupported
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
db0e54 |
try:
|
|
Benjamin Poirier |
2c7d8e |
cache_history = self.cache["history"]
|
|
Benjamin Poirier |
2c7d8e |
except KeyError:
|
|
Benjamin Poirier |
2c7d8e |
raise CInconsistent
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
bcc8a7 |
# This detailed check may be needed if an older git-sort (which
|
|
Benjamin Poirier |
bcc8a7 |
# didn't set a cache version) modified the cache.
|
|
Benjamin Poirier |
600ead |
if (not isinstance(cache_history, list) or
|
|
Benjamin Poirier |
bcc8a7 |
len(cache_history) < 1 or
|
|
Benjamin Poirier |
bcc8a7 |
len(cache_history[0]) != 4 or
|
|
Benjamin Poirier |
600ead |
not isinstance(cache_history[0][3], dict)):
|
|
Benjamin Poirier |
bcc8a7 |
raise CInconsistent
|
|
Benjamin Poirier |
bcc8a7 |
|
|
Benjamin Poirier |
2c7d8e |
return collections.OrderedDict([
|
|
Benjamin Poirier |
2c7d8e |
(
|
|
Benjamin Poirier |
2c7d8e |
(Head(RepoURL(e[0]), e[1]), e[2],),
|
|
Benjamin Poirier |
2c7d8e |
e[3],
|
|
Benjamin Poirier |
2c7d8e |
) for e in cache_history])
|
|
Benjamin Poirier |
2c7d8e |
else:
|
|
Benjamin Poirier |
2c7d8e |
raise KeyError
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
2c7d8e |
def __setitem__(self, key, value):
|
|
Benjamin Poirier |
76bf0c |
"""
|
|
Benjamin Poirier |
2c7d8e |
Supported keys:
|
|
Benjamin Poirier |
2c7d8e |
"history"
|
|
Benjamin Poirier |
2c7d8e |
OrderedDict((Head, sha1) : history)
|
|
Benjamin Poirier |
76bf0c |
"""
|
|
Benjamin Poirier |
2c7d8e |
if self.closed or not self.write_enable:
|
|
Benjamin Poirier |
2c7d8e |
raise ValueError
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
if key == "history":
|
|
Benjamin Poirier |
2c7d8e |
self.cache["history"] = [(
|
|
Benjamin Poirier |
2c7d8e |
repr(desc[0].repo_url), desc[0].rev, desc[1], log,
|
|
Benjamin Poirier |
2c7d8e |
) for desc, log in value.items()]
|
|
Benjamin Poirier |
2c7d8e |
else:
|
|
Benjamin Poirier |
2c7d8e |
raise KeyError
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
395993 |
@functools.total_ordering
|
|
Benjamin Poirier |
395993 |
class IndexedCommit(object):
|
|
Benjamin Poirier |
395993 |
def __init__(self, head, index):
|
|
Benjamin Poirier |
395993 |
self.head = head
|
|
Benjamin Poirier |
395993 |
self.index = index
|
|
Benjamin Poirier |
395993 |
|
|
Benjamin Poirier |
395993 |
|
|
Benjamin Poirier |
395993 |
def _is_valid_operand(self, other):
|
|
Benjamin Poirier |
395993 |
return hasattr(other, "head") and hasattr(other, "index")
|
|
Benjamin Poirier |
395993 |
|
|
Benjamin Poirier |
395993 |
|
|
Benjamin Poirier |
395993 |
def __eq__(self, other):
|
|
Benjamin Poirier |
395993 |
if not self._is_valid_operand(other):
|
|
Benjamin Poirier |
395993 |
return NotImplemented
|
|
Benjamin Poirier |
395993 |
return (self.head == other.head and self.index == other.index)
|
|
Benjamin Poirier |
395993 |
|
|
Benjamin Poirier |
395993 |
|
|
Benjamin Poirier |
395993 |
def __lt__(self, other):
|
|
Benjamin Poirier |
395993 |
if not self._is_valid_operand(other):
|
|
Benjamin Poirier |
395993 |
return NotImplemented
|
|
Benjamin Poirier |
395993 |
if self.head == other.head:
|
|
Benjamin Poirier |
395993 |
return self.index < other.index
|
|
Benjamin Poirier |
395993 |
else:
|
|
Benjamin Poirier |
395993 |
return self.head < other.head
|
|
Benjamin Poirier |
395993 |
|
|
Benjamin Poirier |
395993 |
|
|
Benjamin Poirier |
395993 |
def __hash__(self):
|
|
Benjamin Poirier |
395993 |
return hash((self.head, self.index,))
|
|
Benjamin Poirier |
395993 |
|
|
Benjamin Poirier |
395993 |
|
|
Benjamin Poirier |
395993 |
def __repr__(self):
|
|
Benjamin Poirier |
395993 |
return "%s %d" % (repr(self.head), self.index,)
|
|
Benjamin Poirier |
395993 |
|
|
Benjamin Poirier |
395993 |
|
|
Benjamin Poirier |
2c7d8e |
class SortIndex(object):
|
|
Benjamin Poirier |
2c7d8e |
version_match = re.compile("refs/tags/v(2\.6\.\d+|\d\.\d+)(-rc\d+)?$")
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
897bbc |
|
|
Benjamin Poirier |
2c7d8e |
def __init__(self, repo):
|
|
Benjamin Poirier |
2c7d8e |
self.repo = repo
|
|
Benjamin Poirier |
2c7d8e |
needs_rebuild = False
|
|
Benjamin Poirier |
76bf0c |
try:
|
|
Benjamin Poirier |
2c7d8e |
with Cache() as cache:
|
|
Benjamin Poirier |
2c7d8e |
try:
|
|
Benjamin Poirier |
2c7d8e |
history = cache["history"]
|
|
Benjamin Poirier |
2c7d8e |
except CNeedsRebuild:
|
|
Benjamin Poirier |
2c7d8e |
needs_rebuild = True
|
|
Benjamin Poirier |
15bd1c |
except CNeedsRebuild:
|
|
Benjamin Poirier |
2c7d8e |
needs_rebuild = True
|
|
Benjamin Poirier |
2c7d8e |
except CError as err:
|
|
Benjamin Poirier |
2c7d8e |
print("Error: %s" % (err,), file=sys.stderr)
|
|
Benjamin Poirier |
2c7d8e |
sys.exit(1)
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
2c7d8e |
try:
|
|
Benjamin Poirier |
2c7d8e |
repo_heads = get_heads(repo)
|
|
Benjamin Poirier |
2c7d8e |
except GSError as err:
|
|
Benjamin Poirier |
2c7d8e |
print("Error: %s" % (err,), file=sys.stderr)
|
|
Benjamin Poirier |
2c7d8e |
sys.exit(1)
|
|
Benjamin Poirier |
76bf0c |
|
|
Benjamin Poirier |
600ead |
if needs_rebuild or list(history.keys()) != list(repo_heads.items()):
|
|
Benjamin Poirier |
2c7d8e |
try:
|
|
Benjamin Poirier |
2c7d8e |
history = get_history(repo, repo_heads)
|
|
Benjamin Poirier |
2c7d8e |
except GSError as err:
|
|
Benjamin Poirier |
2c7d8e |
print("Error: %s" % (err,), file=sys.stderr)
|
|
Benjamin Poirier |
2c7d8e |
sys.exit(1)
|
|
Benjamin Poirier |
2c7d8e |
try:
|
|
Benjamin Poirier |
2c7d8e |
with Cache(write_enable=True) as cache:
|
|
Benjamin Poirier |
2c7d8e |
cache["history"] = collections.OrderedDict(
|
|
Benjamin Poirier |
2c7d8e |
[((head, repo_heads[head],), log,)
|
|
Benjamin Poirier |
2c7d8e |
for head, log in history.items()])
|
|
Benjamin Poirier |
2c7d8e |
except CError as err:
|
|
Benjamin Poirier |
2c7d8e |
print("Error: %s" % (err,), file=sys.stderr)
|
|
Benjamin Poirier |
2c7d8e |
sys.exit(1)
|
|
Benjamin Poirier |
2c7d8e |
self.history = history
|
|
Benjamin Poirier |
76bf0c |
else:
|
|
Benjamin Poirier |
2c7d8e |
# no more need for the head sha1
|
|
Benjamin Poirier |
2c7d8e |
self.history = collections.OrderedDict(
|
|
Benjamin Poirier |
2c7d8e |
[(key[0], log,) for key, log in history.items()])
|
|
Benjamin Poirier |
2c7d8e |
self.version_indexes = None
|
|
Benjamin Poirier |
6a71e7 |
self.repo_heads = repo_heads
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
544f5d |
def lookup(self, commit):
|
|
Benjamin Poirier |
544f5d |
for head, log in self.history.items():
|
|
Benjamin Poirier |
544f5d |
try:
|
|
Benjamin Poirier |
544f5d |
index = log[commit]
|
|
Benjamin Poirier |
544f5d |
except KeyError:
|
|
Benjamin Poirier |
544f5d |
continue
|
|
Benjamin Poirier |
544f5d |
else:
|
|
Benjamin Poirier |
395993 |
return IndexedCommit(head, index)
|
|
Benjamin Poirier |
544f5d |
|
|
Benjamin Poirier |
544f5d |
raise GSKeyError
|
|
Benjamin Poirier |
544f5d |
|
|
Benjamin Poirier |
544f5d |
|
|
Benjamin Poirier |
76bf0c |
def sort(self, mapping):
|
|
Benjamin Poirier |
e8d72d |
"""
|
|
Benjamin Poirier |
e8d72d |
Returns an OrderedDict
|
|
Benjamin Poirier |
e8d72d |
result[Head][]
|
|
Benjamin Poirier |
e8d72d |
sorted values from the mapping which are found in Head
|
|
Benjamin Poirier |
e8d72d |
"""
|
|
Benjamin Poirier |
e8d72d |
result = collections.OrderedDict([(head, [],) for head in self.history])
|
|
Benjamin Poirier |
600ead |
for commit in list(mapping.keys()):
|
|
Benjamin Poirier |
544f5d |
try:
|
|
Benjamin Poirier |
395993 |
ic = self.lookup(commit)
|
|
Benjamin Poirier |
544f5d |
except GSKeyError:
|
|
Benjamin Poirier |
544f5d |
continue
|
|
Benjamin Poirier |
544f5d |
else:
|
|
Benjamin Poirier |
395993 |
result[ic.head].append((ic.index, mapping.pop(commit),))
|
|
Benjamin Poirier |
e8d72d |
|
|
Benjamin Poirier |
e8d72d |
for head, entries in result.items():
|
|
Benjamin Poirier |
e8d72d |
entries.sort(key=operator.itemgetter(0))
|
|
Benjamin Poirier |
e8d72d |
result[head] = [e[1] for e in entries]
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
e8d72d |
return result
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
0abe03 |
def describe(self, index):
|
|
Benjamin Poirier |
0abe03 |
"""
|
|
Benjamin Poirier |
0abe03 |
index must come from the mainline head (remotes[0]).
|
|
Benjamin Poirier |
0abe03 |
"""
|
|
Benjamin Poirier |
0abe03 |
if self.version_indexes is None:
|
|
Benjamin Poirier |
0abe03 |
history = self.history[remotes[0]]
|
|
Benjamin Poirier |
cf4f00 |
# Remove "refs/tags/"
|
|
Benjamin Poirier |
cf4f00 |
# Mainline release tags are annotated tag objects attached to a
|
|
Benjamin Poirier |
cf4f00 |
# commit object; do not consider other kinds of tags.
|
|
Benjamin Poirier |
cf4f00 |
objects = [(obj_tag.get_object(), tag,)
|
|
Benjamin Poirier |
cf4f00 |
for obj_tag, tag in [
|
|
Benjamin Poirier |
cf4f00 |
(self.repo.revparse_single(tag), tag[10:],)
|
|
Benjamin Poirier |
cf4f00 |
for tag in self.repo.listall_references()
|
|
Benjamin Poirier |
cf4f00 |
if self.version_match.match(tag)
|
|
Benjamin Poirier |
cf4f00 |
] if obj_tag.type == pygit2.GIT_OBJ_TAG]
|
|
Benjamin Poirier |
0abe03 |
revs = [(history[str(obj.id)], tag,)
|
|
Benjamin Poirier |
0abe03 |
for obj, tag in objects
|
|
Benjamin Poirier |
0abe03 |
if obj.type == pygit2.GIT_OBJ_COMMIT]
|
|
Benjamin Poirier |
0abe03 |
revs.sort(key=operator.itemgetter(0))
|
|
Benjamin Poirier |
600ead |
self.version_indexes = list(zip(*revs))
|
|
Benjamin Poirier |
0abe03 |
|
|
Benjamin Poirier |
535548 |
if not self.version_indexes:
|
|
Benjamin Poirier |
535548 |
raise GSError("Cannot describe commit, did not find any mainline "
|
|
Benjamin Poirier |
535548 |
"release tags in repository.")
|
|
Benjamin Poirier |
535548 |
|
|
Benjamin Poirier |
0abe03 |
indexes, tags = self.version_indexes
|
|
Benjamin Poirier |
0abe03 |
i = bisect.bisect_left(indexes, index)
|
|
Benjamin Poirier |
0abe03 |
if i == len(tags):
|
|
Benjamin Poirier |
0abe03 |
# not yet part of a tagged release
|
|
Michal Suchanek |
7dce3d |
m = re.search("v([0-9]+)\.([0-9]+)(|-rc([0-9]+))$", tags[-1])
|
|
Michal Suchanek |
7dce3d |
if m:
|
|
Michal Suchanek |
7dce3d |
# Post-release commit with no rc, it'll be rc1
|
|
Michal Suchanek |
7dce3d |
if m.group(3) == "":
|
|
Michal Suchanek |
7dce3d |
nexttag = "v%s.%d-rc1" % (m.group(1), int(m.group(2)) + 1)
|
|
Michal Suchanek |
7dce3d |
else:
|
|
Michal Suchanek |
7dce3d |
nexttag = "v%s.%d or v%s.%s-rc%d (next release)" % \
|
|
Michal Kubecek |
4f0865 |
(m.group(1), int(m.group(2)), m.group(1),
|
|
Michal Suchanek |
7dce3d |
m.group(2), int(m.group(4)) + 1)
|
|
Michal Suchanek |
7dce3d |
return nexttag
|
|
Benjamin Poirier |
0abe03 |
else:
|
|
Benjamin Poirier |
0abe03 |
return tags[i]
|
|
Benjamin Poirier |
0abe03 |
|
|
Benjamin Poirier |
0abe03 |
|
|
Benjamin Poirier |
0aaea3 |
if __name__ == "__main__":
|
|
Benjamin Poirier |
0aaea3 |
parser = argparse.ArgumentParser(
|
|
Benjamin Poirier |
0aaea3 |
description="Sort input lines according to the upstream order of "
|
|
Benjamin Poirier |
0aaea3 |
"commits that each line represents, with the first word on the line "
|
|
Benjamin Poirier |
0aaea3 |
"taken to be a commit id.")
|
|
Benjamin Poirier |
0aaea3 |
parser.add_argument("-d", "--dump-heads", action="store_true",
|
|
Benjamin Poirier |
0aaea3 |
help="Print the branch heads used for sorting "
|
|
Benjamin Poirier |
0aaea3 |
"(debugging).")
|
|
Benjamin Poirier |
0aaea3 |
args = parser.parse_args()
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
0aaea3 |
try:
|
|
Benjamin Poirier |
0aaea3 |
path = os.environ["GIT_DIR"]
|
|
Benjamin Poirier |
0aaea3 |
except KeyError:
|
|
Benjamin Poirier |
4f8279 |
try:
|
|
Benjamin Poirier |
4f8279 |
path = pygit2.discover_repository(os.getcwd())
|
|
Benjamin Poirier |
4f8279 |
except KeyError:
|
|
Benjamin Poirier |
4f8279 |
print("Error: Not a git repository", file=sys.stderr)
|
|
Benjamin Poirier |
4f8279 |
sys.exit(1)
|
|
Benjamin Poirier |
0aaea3 |
repo = pygit2.Repository(path)
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
0aaea3 |
if args.dump_heads:
|
|
Benjamin Poirier |
2c7d8e |
needs_rebuild = False
|
|
Benjamin Poirier |
0aaea3 |
try:
|
|
Benjamin Poirier |
2c7d8e |
with Cache() as cache:
|
|
Benjamin Poirier |
2c7d8e |
try:
|
|
Benjamin Poirier |
2c7d8e |
print("Cached heads (version %d):" % cache["version"])
|
|
Benjamin Poirier |
2c7d8e |
except CKeyError:
|
|
Benjamin Poirier |
2c7d8e |
print("No usable cache")
|
|
Benjamin Poirier |
2c7d8e |
needs_rebuild = True
|
|
Benjamin Poirier |
2c7d8e |
else:
|
|
Benjamin Poirier |
2c7d8e |
try:
|
|
Benjamin Poirier |
2c7d8e |
history = cache["history"]
|
|
Benjamin Poirier |
2c7d8e |
except CUnsupported:
|
|
Benjamin Poirier |
2c7d8e |
print("Unsupported cache version")
|
|
Benjamin Poirier |
2c7d8e |
needs_rebuild = True
|
|
Benjamin Poirier |
2c7d8e |
except CInconsistent:
|
|
Benjamin Poirier |
2c7d8e |
print("Inconsistent cache content")
|
|
Benjamin Poirier |
2c7d8e |
needs_rebuild = True
|
|
Benjamin Poirier |
2c7d8e |
else:
|
|
Benjamin Poirier |
600ead |
pprint.pprint(list(history.keys()))
|
|
Benjamin Poirier |
600ead |
except CAbsent:
|
|
Benjamin Poirier |
4f1bbb |
print("No usable cache")
|
|
Benjamin Poirier |
2c7d8e |
needs_rebuild = True
|
|
Benjamin Poirier |
600ead |
except CNeedsRebuild:
|
|
Benjamin Poirier |
600ead |
needs_rebuild = True
|
|
Benjamin Poirier |
2c7d8e |
except CError as err:
|
|
Benjamin Poirier |
2c7d8e |
print("Error: %s" % (err,), file=sys.stderr)
|
|
Benjamin Poirier |
2c7d8e |
sys.exit(1)
|
|
Benjamin Poirier |
2c7d8e |
|
|
Benjamin Poirier |
2c7d8e |
try:
|
|
Benjamin Poirier |
2c7d8e |
repo_heads = get_heads(repo)
|
|
Benjamin Poirier |
2c7d8e |
except GSError as err:
|
|
Benjamin Poirier |
2c7d8e |
print("Error: %s" % (err,), file=sys.stderr)
|
|
Benjamin Poirier |
2c7d8e |
sys.exit(1)
|
|
Benjamin Poirier |
600ead |
if not needs_rebuild and list(history.keys()) != list(repo_heads.items()):
|
|
Benjamin Poirier |
2c7d8e |
needs_rebuild = True
|
|
Benjamin Poirier |
2c7d8e |
print("Current heads (version %d):" % Cache.version)
|
|
Benjamin Poirier |
600ead |
pprint.pprint(list(repo_heads.items()))
|
|
Benjamin Poirier |
2c7d8e |
if needs_rebuild:
|
|
Benjamin Poirier |
2c7d8e |
action = "Will"
|
|
Benjamin Poirier |
4f1bbb |
else:
|
|
Benjamin Poirier |
0aaea3 |
action = "Will not"
|
|
Benjamin Poirier |
0aaea3 |
print("%s rebuild history" % (action,))
|
|
Benjamin Poirier |
0aaea3 |
sys.exit(0)
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
2c7d8e |
index = SortIndex(repo)
|
|
Benjamin Poirier |
395993 |
dest = {}
|
|
Benjamin Poirier |
395993 |
oot = []
|
|
Benjamin Poirier |
0aaea3 |
num = 0
|
|
Benjamin Poirier |
0aaea3 |
for line in sys.stdin.readlines():
|
|
Benjamin Poirier |
0aaea3 |
num = num + 1
|
|
Benjamin Poirier |
51f0b8 |
tokens = line.strip().split(None, 1)
|
|
Benjamin Poirier |
51f0b8 |
if not tokens:
|
|
Benjamin Poirier |
51f0b8 |
continue
|
|
Benjamin Poirier |
0aaea3 |
try:
|
|
Benjamin Poirier |
51f0b8 |
commit = repo.revparse_single(tokens[0])
|
|
Benjamin Poirier |
0aaea3 |
except ValueError:
|
|
Benjamin Poirier |
0aaea3 |
print("Error: did not find a commit hash on line %d:\n%s" %
|
|
Benjamin Poirier |
0aaea3 |
(num, line.strip(),), file=sys.stderr)
|
|
Benjamin Poirier |
0aaea3 |
sys.exit(1)
|
|
Benjamin Poirier |
0aaea3 |
except KeyError:
|
|
Benjamin Poirier |
0aaea3 |
print("Error: commit hash on line %d not found in the repository:\n%s" %
|
|
Benjamin Poirier |
0aaea3 |
(num, line.strip(),), file=sys.stderr)
|
|
Benjamin Poirier |
0aaea3 |
sys.exit(1)
|
|
Benjamin Poirier |
0aaea3 |
h = str(commit.id)
|
|
Benjamin Poirier |
395993 |
if h in dest:
|
|
Benjamin Poirier |
395993 |
dest[h][1].append(line)
|
|
Benjamin Poirier |
0aaea3 |
else:
|
|
Benjamin Poirier |
395993 |
try:
|
|
Benjamin Poirier |
395993 |
ic = index.lookup(h)
|
|
Benjamin Poirier |
395993 |
except GSKeyError:
|
|
Benjamin Poirier |
395993 |
oot.append(line)
|
|
Benjamin Poirier |
395993 |
else:
|
|
Benjamin Poirier |
395993 |
dest[h] = (ic, [line],)
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
e8d72d |
print("".join([line
|
|
Benjamin Poirier |
395993 |
for ic, lines in sorted(dest.values(),
|
|
Benjamin Poirier |
395993 |
key=operator.itemgetter(0))
|
|
Benjamin Poirier |
395993 |
for line in lines
|
|
Benjamin Poirier |
e8d72d |
]), end="")
|
|
Benjamin Poirier |
0aaea3 |
|
|
Benjamin Poirier |
395993 |
if oot:
|
|
Benjamin Poirier |
0aaea3 |
print("Error: the following entries were not found in the indexed heads:",
|
|
Benjamin Poirier |
0aaea3 |
file=sys.stderr)
|
|
Benjamin Poirier |
395993 |
print("".join(oot), end="")
|
|
Benjamin Poirier |
0aaea3 |
sys.exit(1)
|