59cd49 | 2010-09-05 | Marcus Comstedt | | #! /usr/bin/env pike
|
c52896 | 2010-09-26 | Marcus Comstedt | | #define DOLLAR "$"
constant unexpanded_id = DOLLAR"Id"DOLLAR;
|
0c9c48 | 2010-09-25 | Marcus Comstedt | | mapping(string:program) hooks = ([
|
517918 | 2010-09-25 | Marcus Comstedt | | "pre-commit" : PreCommitHook,
|
efde06 | 2010-09-25 | Marcus Comstedt | | "pre-receive" : PreReceiveHook,
|
fa4f8f | 2010-09-27 | Marcus Comstedt | | "post-commit" : PostCommitHook,
|
c21d88 | 2010-10-04 | Marcus Comstedt | | "post-rewrite" : PostRewriteHook,
|
59cd49 | 2010-09-05 | Marcus Comstedt | | ]);
|
0c9c48 | 2010-09-25 | Marcus Comstedt | | mapping(string:program) filters = ([
|
7b6b1b | 2010-09-25 | Marcus Comstedt | | #if 0
|
59cd49 | 2010-09-05 | Marcus Comstedt | | "nice_ident" : NiceIdentFilter,
|
7b6b1b | 2010-09-25 | Marcus Comstedt | | #endif
|
59cd49 | 2010-09-05 | Marcus Comstedt | | ]);
constant filterops = ({ "clean", "smudge" });
void fail(string msg, mixed ... args)
{
werror(msg, @args);
exit(1);
}
void iofailn(int errno, string msg, mixed ... args)
{
fail(msg+": %s\n", @args, strerror(errno));
}
void iofail(string msg, mixed ... args)
{
iofailn(errno(), msg, @args);
}
|
0c9c48 | 2010-09-25 | Marcus Comstedt | | array(string) split_z(string data)
{
array(string) a = data / "\0";
if (sizeof(a) && a[-1] == "")
a = a[..sizeof(a)-2];
return a;
}
|
bb7262 | 2010-09-26 | Marcus Comstedt | | array(string) split_lf(string data)
{
array(string) a = data / "\n";
if (sizeof(a) && a[-1] == "")
a = a[..sizeof(a)-2];
return a;
}
|
59cd49 | 2010-09-05 | Marcus Comstedt | | string run_git_ex(int max_exitcode, string ... args)
{
|
404f40 | 2010-10-04 | Martin Stjernholm | | Stdio.File mystdout = Stdio.File();
Stdio.File mystderr = Stdio.File();
Process.Process p = Process.Process (({"git"})+args, ([
"stdout":mystdout->pipe(),
"stderr":mystderr->pipe(),
]));
string gotstdout="", gotstderr="";
mystdout->set_read_callback( lambda( mixed i, string data) {
gotstdout += data;
} );
mystderr->set_read_callback( lambda( mixed i, string data) {
gotstderr += data;
} );
|
4a53ba | 2010-11-01 | Marcus Comstedt | | mystdout->set_close_callback( lambda () {
mystdout->set_read_callback(0);
mystdout = 0;
});
mystderr->set_close_callback( lambda () {
mystderr->set_read_callback(0);
mystderr = 0;
});
while( mystdout || mystderr )
|
404f40 | 2010-10-04 | Martin Stjernholm | | Pike.DefaultBackend( 1.0 );
int exitcode = p->wait();
if (exitcode > max_exitcode) {
werror(gotstderr);
fail("git exited with code %d\n", exitcode);
|
59cd49 | 2010-09-05 | Marcus Comstedt | | }
|
404f40 | 2010-10-04 | Martin Stjernholm | | return gotstdout;
|
59cd49 | 2010-09-05 | Marcus Comstedt | | }
string run_git(string ... args)
{
return run_git_ex(0, @args);
}
|
b668f1 | 2010-09-26 | Marcus Comstedt | | string get_staged_file(string filename, int|void allow_empty)
|
efde06 | 2010-09-25 | Marcus Comstedt | | {
|
b668f1 | 2010-09-26 | Marcus Comstedt | | string blob;
string treeentry = run_git("ls-files", "--stage", "--", filename);
if (allow_empty && !sizeof(treeentry))
return "";
if (2 != sscanf(treeentry, "%*o %s ", blob))
|
efde06 | 2010-09-25 | Marcus Comstedt | | fail("Unable to parse output from git ls-files...\n");
|
b668f1 | 2010-09-26 | Marcus Comstedt | | return run_git("cat-file", "blob", blob);
|
efde06 | 2010-09-25 | Marcus Comstedt | | }
|
59cd49 | 2010-09-05 | Marcus Comstedt | |
|
aeab9a | 2010-09-25 | Marcus Comstedt | | string get_committed_file(string sha, string filename, int|void allow_empty)
|
efde06 | 2010-09-25 | Marcus Comstedt | | {
string blob;
|
b668f1 | 2010-09-26 | Marcus Comstedt | | string treeentry = run_git("ls-tree", sha, "--", filename);
if (allow_empty && !sizeof(treeentry))
|
aeab9a | 2010-09-25 | Marcus Comstedt | | return "";
|
c275d4 | 2011-01-23 | Marcus Comstedt | | if (allow_empty == 2 && 2 == sscanf(treeentry, "%*o tree %s\t", blob))
return "";
|
b668f1 | 2010-09-26 | Marcus Comstedt | | if (2 != sscanf(treeentry, "%*o blob %s\t", blob))
|
efde06 | 2010-09-25 | Marcus Comstedt | | fail("Unexpected output from git ls-tree\n");
return run_git("cat-file", "blob", blob);
}
|
517918 | 2010-09-25 | Marcus Comstedt | |
|
e6367f | 2010-10-02 | Marcus Comstedt | | string check_commit_timestamps(string commit)
{
int cct, cat, pct, pat;
string parents;
int sysclock = time() + 60;
if(3 != sscanf(run_git("log", "-n", "1", "--format=%ct %at %P", commit),
"%d %d %s\n", cct, cat, parents))
fail("Unexpected output from git log");
if (cat > sysclock)
return "Author time is in the future";
if (cct > sysclock)
return "Commit time is in the future";
if (cat > cct)
return "Author time is later than commit time";
foreach(parents/" ", string parent) {
if(2 != sscanf(run_git("log", "-n", "1", "--format=%ct %at", parent),
"%d %d", pct, pat))
fail("Unexpected output from git log");
if (cct < pct)
return "Commit time is before that of parent "+parent;
}
return 0;
}
|
bdb978 | 2010-10-03 | Marcus Comstedt | | int is_encoding_utf8(string name)
{
return (!name) || (<"utf-8", "utf8">)[lower_case(name)];
}
string check_encoding(string data, string|void encoding)
{
if(is_encoding_utf8(encoding))
encoding = "UTF-8";
mixed err = catch {
Locale.Charset.Decoder decoder = Locale.Charset.decoder(encoding);
|
3b6d0f | 2010-10-03 | Marcus Comstedt | | foreach(Array.uniq(values(decoder->feed(data)->drain())), int c)
switch(c) {
case 0xfffd:
return "Undefinied character detected\n";
case '\t':
case '\n':
case '\r':
break;
default:
if (c<32 || (c>=0x80 && c<0xa0))
return sprintf("Forbidden control character 0x%02x detected\n", c);
}
|
bdb978 | 2010-10-03 | Marcus Comstedt | | };
return err && err[0];
}
string check_commit_msg(string commit)
{
string message = run_git("cat-file", "commit", commit);
string encoding = 0;
string headers = (message/"\n\n")[0];
foreach(headers/"\n", string headerline) {
if(has_prefix(headerline, "encoding "))
encoding = headerline[9..];
}
return check_encoding(message, encoding);
}
|
efde06 | 2010-09-25 | Marcus Comstedt | | class GitAttributes
|
59cd49 | 2010-09-05 | Marcus Comstedt | | {
|
efde06 | 2010-09-25 | Marcus Comstedt | | enum {
ATTR_TRUE = 1,
ATTR_FALSE = 2,
ATTR_UNSET = 3
};
class AttrState(string attr, string|int setto) {
static string _sprintf(int type) {
return type=='O' && sprintf("AttrState(%O, %O)\n", attr, setto);
}
};
class MatchAttr(string name, int is_macro, array(AttrState) states) {
static string _sprintf(int type) {
return type=='O' && sprintf("MatchAttr(%O, %d, %O)\n",
name, is_macro, states);
}
};
static array(MatchAttr) attrs = ({});
|
8b2c1e | 2010-09-27 | Marcus Comstedt | | static mapping(string:MatchAttr) macros = ([]);
|
efde06 | 2010-09-25 | Marcus Comstedt | |
static int invalid_attr_name(string name)
|
08686f | 2010-09-25 | Marcus Comstedt | | {
|
efde06 | 2010-09-25 | Marcus Comstedt | | int n;
if(name == "" || name[0] == '-')
return 1;
sscanf(name, "%*[-._0-9a-zA-Z]%n", n);
return n != sizeof(name);
|
08686f | 2010-09-25 | Marcus Comstedt | | }
|
efde06 | 2010-09-25 | Marcus Comstedt | | static AttrState parse_attr(string src)
|
0c9c48 | 2010-09-25 | Marcus Comstedt | | {
|
efde06 | 2010-09-25 | Marcus Comstedt | | string equals = 0;
string|int setto;
sscanf(src, "%s=%s", src, equals);
if(src[0] == '-' || src[0] == '!') {
setto = (src[0]=='-'? ATTR_FALSE : ATTR_UNSET);
src = src[1..];
} else
setto = equals || ATTR_TRUE;
if(invalid_attr_name(src))
fail("%s is not a valid attribute name\n", src);
return AttrState(src, setto);
}
|
0c9c48 | 2010-09-25 | Marcus Comstedt | |
|
efde06 | 2010-09-25 | Marcus Comstedt | | static MatchAttr parse_attr_line(string line, int macro_ok)
{
int is_macro=0;
string name;
line = String.trim_whites(replace(line, ({"\t","\r","\n"}),
({" ", " ", " "})));
if(!sizeof(line) || line[0] == '#')
return 0;
if(has_prefix(line, "[attr]")) {
if(!macro_ok)
fail("%s not allowed\n", name);
is_macro=1;
sscanf(line, "[attr]%*[ ]%s%*[ ]%s", name, line);
} else {
sscanf(line, "%s%*[ ]%s", name, line);
}
|
19bf73 | 2010-09-26 | Marcus Comstedt | | return MatchAttr(name, is_macro, reverse(map(line/" "-({""}), parse_attr)));
|
efde06 | 2010-09-25 | Marcus Comstedt | | }
static void handle_attr_line(string line, int macro_ok)
{
MatchAttr a = parse_attr_line(line, macro_ok);
if(a) attrs += ({ a });
}
static void create(string data)
{
foreach(data/"\n", string line)
handle_attr_line(line, 1);
|
19bf73 | 2010-09-26 | Marcus Comstedt | | attrs = reverse(attrs);
|
8b2c1e | 2010-09-27 | Marcus Comstedt | | foreach(attrs, MatchAttr a)
if(a->is_macro)
macros[a->name] = a;
attrs = filter(attrs, lambda(MatchAttr a) { return !a->is_macro; });
|
efde06 | 2010-09-25 | Marcus Comstedt | | }
static int path_matches(string path, string pattern)
{
if(search(pattern, "/")<0)
return glob(pattern, (path/"/")[-1]);
if(has_prefix(pattern, "/"))
pattern = pattern[1..];
return glob(pattern, path);
}
static void macroexpand_one(string attrname, array(MatchAttr) attrs,
mapping(string:string|int) all_attr)
{
if(all_attr[attrname] != ATTR_TRUE)
return;
|
8b2c1e | 2010-09-27 | Marcus Comstedt | | MatchAttr ma = macros[attrname];
|
19bf73 | 2010-09-26 | Marcus Comstedt | | if(ma) fill_one(ma, attrs, all_attr);
|
efde06 | 2010-09-25 | Marcus Comstedt | | }
static void fill_one(MatchAttr attr, array(MatchAttr) attrs,
mapping(string:string|int) all_attr)
{
|
19bf73 | 2010-09-26 | Marcus Comstedt | | foreach(attr->states, AttrState s) {
|
efde06 | 2010-09-25 | Marcus Comstedt | | if(!all_attr[s->attr]) {
all_attr[s->attr] = s->setto;
macroexpand_one(s->attr, attrs, all_attr);
}
|
0c9c48 | 2010-09-25 | Marcus Comstedt | | }
}
|
efde06 | 2010-09-25 | Marcus Comstedt | | static void fill(string path, array(MatchAttr) attrs,
mapping(string:string|int) all_attr)
{
|
19bf73 | 2010-09-26 | Marcus Comstedt | | foreach(attrs, MatchAttr a)
|
8b2c1e | 2010-09-27 | Marcus Comstedt | | if( path_matches(path, a->name))
|
19bf73 | 2010-09-26 | Marcus Comstedt | | fill_one(a, attrs, all_attr);
|
efde06 | 2010-09-25 | Marcus Comstedt | | }
mapping(string:string|int) checkattr(string path)
{
mapping(string:string|int) all_attr = ([]);
fill(path, attrs, all_attr);
return all_attr;
}
|
aeab9a | 2010-09-25 | Marcus Comstedt | | array(string) findattr(string attrname)
{
array(string) r = ({});
|
8b2c1e | 2010-09-27 | Marcus Comstedt | | foreach(attrs+values(macros), MatchAttr attr) {
|
aeab9a | 2010-09-25 | Marcus Comstedt | | int z=0;
foreach(attr->states, AttrState state)
if(state->attr == attrname && state->setto == ATTR_TRUE) {
z = 1;
break;
}
if (z)
r += ({ attr->name });
}
return r;
}
|
efde06 | 2010-09-25 | Marcus Comstedt | | static string _sprintf(int type) {
return type=='O' && sprintf("GitAttributes(%O)\n", attrs);
}
}
class CommitHookUtils
{
|
d64580 | 2010-09-26 | Marcus Comstedt | | static array(string) files_to_commit;
|
b668f1 | 2010-09-26 | Marcus Comstedt | | GitAttributes attrs;
|
d64580 | 2010-09-26 | Marcus Comstedt | |
|
b668f1 | 2010-09-26 | Marcus Comstedt | | string get_file(string filename, int|void allow_empty);
|
2c8b30 | 2010-09-26 | Marcus Comstedt | | string get_old_file(string filename, int|void allow_empty);
|
b668f1 | 2010-09-26 | Marcus Comstedt | | int entry_is_new(string filename) { return 0; }
|
d64580 | 2010-09-26 | Marcus Comstedt | |
|
0ef2fc | 2010-09-25 | Marcus Comstedt | | int find_expanded_ident(string data)
|
08686f | 2010-09-25 | Marcus Comstedt | | {
int p=0;
|
c52896 | 2010-09-26 | Marcus Comstedt | | while ((p = search(data, DOLLAR"Id", p))>=0) {
|
4409b4 | 2011-04-25 | Martin Stjernholm | | if (data[p..p+3] != unexpanded_id) {
int p2 = search(data, DOLLAR, p+3), p3 = search(data, "\n", p+3);
if (p2 > p && (p3 < 0 || p2 < p3))
return 1;
}
|
0ef2fc | 2010-09-25 | Marcus Comstedt | | p += 4;
}
return 0;
}
|
efde06 | 2010-09-25 | Marcus Comstedt | |
|
d64580 | 2010-09-26 | Marcus Comstedt | | int check_ident(string filename)
|
efde06 | 2010-09-25 | Marcus Comstedt | | {
|
c275d4 | 2011-01-23 | Marcus Comstedt | | if (find_expanded_ident(get_file(filename, 2))) {
|
d64580 | 2010-09-26 | Marcus Comstedt | | write("File %s contains an expanded ident.\n", filename);
if(this_program == PreCommitHook) {
write("Try 'git reset %s; git add %s', "
"or remove the ident manually.\n",
@({filename})*2);;
}
return 1;
}
return 0;
|
efde06 | 2010-09-25 | Marcus Comstedt | | }
|
b668f1 | 2010-09-26 | Marcus Comstedt | |
int check_blocker_attributes()
{
|
2c9514 | 2010-10-10 | Martin Stjernholm | | int err = 0;
|
b668f1 | 2010-09-26 | Marcus Comstedt | | foreach(files_to_commit, string filename) {
mapping(string:string|int) a = attrs->checkattr(filename);
if(a->foreign_ident == GitAttributes.ATTR_TRUE) {
if (!entry_is_new(filename)) {
write("File %s has the foreign_ident attribute. Please remove it before commit.\n", filename);
|
2c9514 | 2010-10-10 | Martin Stjernholm | | err = 1;
continue;
|
b668f1 | 2010-09-26 | Marcus Comstedt | | }
}
if(stringp(a->block_commit) || a->block_commit == GitAttributes.ATTR_TRUE) {
if (!entry_is_new(filename)) {
write("File %s is blocked from committing: %s\n", filename,
replace((stringp(a->block_commit)? a->block_commit :
"no explanation given"), "-", " "));
|
2c9514 | 2010-10-10 | Martin Stjernholm | | err = 1;
continue;
|
b668f1 | 2010-09-26 | Marcus Comstedt | | }
}
if(a->ident && a->ident != GitAttributes.ATTR_FALSE &&
a->ident != GitAttributes.ATTR_UNSET) {
|
2c9514 | 2010-10-10 | Martin Stjernholm | | if (check_ident(filename)) {
err = 1;
continue;
}
|
b668f1 | 2010-09-26 | Marcus Comstedt | | }
}
|
2c9514 | 2010-10-10 | Martin Stjernholm | | return err;
|
b668f1 | 2010-09-26 | Marcus Comstedt | | }
|
2c8b30 | 2010-09-26 | Marcus Comstedt | |
int check_gitattributes_files()
{
|
2c9514 | 2010-10-10 | Martin Stjernholm | | int err = 0;
|
2c8b30 | 2010-09-26 | Marcus Comstedt | | foreach(files_to_commit, string filename)
if(has_suffix(filename, "/.gitattributes")) {
|
276861 | 2010-10-10 | Martin Stjernholm | | write(".gitattributes are not allowed in subdirectories; "
"please remove %s\n", filename);
|
2c9514 | 2010-10-10 | Martin Stjernholm | | err = 1;
|
2c8b30 | 2010-09-26 | Marcus Comstedt | | }
if(search(files_to_commit, ".gitattributes")>=0) {
GitAttributes old_attrs =
GitAttributes(get_old_file(".gitattributes", 1));
|
248167 | 2010-09-26 | Marcus Comstedt | | array(string) new_f_i = sort(attrs->findattr("foreign_ident"));
array(string) old_f_i = sort(old_attrs->findattr("foreign_ident"));
array(string) added_fi = new_f_i - old_f_i;
array(string) removed_fi = old_f_i - new_f_i;
|
2c8b30 | 2010-09-26 | Marcus Comstedt | |
|
248167 | 2010-09-26 | Marcus Comstedt | | foreach(added_fi, string path) {
|
2c8b30 | 2010-09-26 | Marcus Comstedt | | if(!has_prefix(path, "/") || search(path, "*")>=0) {
|
276861 | 2010-10-10 | Martin Stjernholm | | write("Commit adds unsupported foreign_ident: %s\n", path);
|
2c9514 | 2010-10-10 | Martin Stjernholm | | err = 1;
continue;
|
2c8b30 | 2010-09-26 | Marcus Comstedt | | }
path = path[1..];
if (!entry_is_new(path)) {
|
276861 | 2010-10-10 | Martin Stjernholm | | write("Commit adds foreign_ident to existing file %s\n", path);
|
2c9514 | 2010-10-10 | Martin Stjernholm | | err = 1;
|
2c8b30 | 2010-09-26 | Marcus Comstedt | | }
}
|
248167 | 2010-09-26 | Marcus Comstedt | | foreach(removed_fi, string path) {
|
2c8b30 | 2010-09-26 | Marcus Comstedt | | if(has_prefix(path, "/"))
path = path[1..];
|
c4a083 | 2010-10-05 | Marcus Comstedt | | if (search(files_to_commit, path)<0 &&
|
ce6264 | 2013-01-23 | Marcus Comstedt | | find_expanded_ident(get_file(path, 2))) {
|
276861 | 2010-10-10 | Martin Stjernholm | | write("Commit removes foreign_ident from unchanged file %s\n", path);
|
2c9514 | 2010-10-10 | Martin Stjernholm | | err = 1;
|
2c8b30 | 2010-09-26 | Marcus Comstedt | | }
}
}
|
2c9514 | 2010-10-10 | Martin Stjernholm | | return err;
|
2c8b30 | 2010-09-26 | Marcus Comstedt | | }
|
efde06 | 2010-09-25 | Marcus Comstedt | | }
|
c0b2b3 | 2010-10-04 | Marcus Comstedt | | class CommitHookUtilsRepo
{
inherit CommitHookUtils;
static string sha;
string get_file(string filename, int|void allow_empty)
{
return get_committed_file(sha, filename, allow_empty);
}
string get_old_file(string filename, int|void allow_empty)
{
return get_committed_file(sha+"^", filename, allow_empty);
}
int entry_is_new(string filename)
{
return (!sizeof(run_git("ls-tree", sha+"^", "--", filename))) &&
sizeof(run_git("ls-tree", sha, "--", filename));
}
int check_commit(string sha)
{
if (!has_prefix(sha, "HEAD"))
write("Checking commit %s\n", sha);
this_program::sha = sha;
files_to_commit =
split_z(run_git("diff", "--name-only", "-z", sha, sha+"^"));
attrs = GitAttributes(get_file(".gitattributes", 1));
string ts_test = check_commit_timestamps(sha);
|
2c9514 | 2010-10-10 | Martin Stjernholm | | int err = 0;
|
c0b2b3 | 2010-10-04 | Marcus Comstedt | | if (ts_test) {
write("Invalid timestamps: %s\n", ts_test);
|
2c9514 | 2010-10-10 | Martin Stjernholm | | err = 1;
|
c0b2b3 | 2010-10-04 | Marcus Comstedt | | }
string cm_test = check_commit_msg(sha);
if (cm_test) {
write("Commit message encoding problem:\n%s", cm_test);
|
2c9514 | 2010-10-10 | Martin Stjernholm | | err = 1;
|
c0b2b3 | 2010-10-04 | Marcus Comstedt | | }
|
2c9514 | 2010-10-10 | Martin Stjernholm | | return check_blocker_attributes() |
check_gitattributes_files() |
err;
|
c0b2b3 | 2010-10-04 | Marcus Comstedt | | }
}
|
efde06 | 2010-09-25 | Marcus Comstedt | |
class PreCommitHook
{
inherit CommitHookUtils;
|
b668f1 | 2010-09-26 | Marcus Comstedt | | string get_file(string filename, int|void allow_empty)
|
d64580 | 2010-09-26 | Marcus Comstedt | | {
|
b668f1 | 2010-09-26 | Marcus Comstedt | | return get_staged_file(filename, allow_empty);
|
d64580 | 2010-09-26 | Marcus Comstedt | | }
|
2c8b30 | 2010-09-26 | Marcus Comstedt | | string get_old_file(string filename, int|void allow_empty)
{
return get_committed_file("HEAD", filename, allow_empty);
}
|
efde06 | 2010-09-25 | Marcus Comstedt | | int check_attributes_staged()
{
|
12fbdc | 2010-10-02 | Marcus Comstedt | | if (sizeof(run_git("diff", "--name-only", "--", ".gitattributes"))) {
|
efde06 | 2010-09-25 | Marcus Comstedt | | write("You have unstaged changes to .gitattributes.\n"
"Please add or stash them before commit.\n");
return 1;
}
}
|
0ef2fc | 2010-09-25 | Marcus Comstedt | |
|
0c9c48 | 2010-09-25 | Marcus Comstedt | | int hook()
{
|
d64580 | 2010-09-26 | Marcus Comstedt | | files_to_commit =
|
0c9c48 | 2010-09-25 | Marcus Comstedt | | split_z(run_git("diff", "--staged", "--name-only", "-z"));
|
b668f1 | 2010-09-26 | Marcus Comstedt | | attrs = GitAttributes(get_file(".gitattributes", 1));
|
0c9c48 | 2010-09-25 | Marcus Comstedt | | return
|
2c9514 | 2010-10-10 | Martin Stjernholm | | check_attributes_staged() |
check_blocker_attributes() |
|
d64580 | 2010-09-26 | Marcus Comstedt | | check_gitattributes_files();
|
0c9c48 | 2010-09-25 | Marcus Comstedt | | }
|
59cd49 | 2010-09-05 | Marcus Comstedt | | }
|
efde06 | 2010-09-25 | Marcus Comstedt | |
class PreReceiveHook
{
|
c0b2b3 | 2010-10-04 | Marcus Comstedt | | inherit CommitHookUtilsRepo;
|
efde06 | 2010-09-25 | Marcus Comstedt | |
|
4594fa | 2010-10-04 | Marcus Comstedt | | static array(string) commits_to_check = ({});
|
e2915a | 2011-03-20 | Marcus Comstedt | | int check_access(string ref_name, string user)
|
efde06 | 2010-09-25 | Marcus Comstedt | | {
|
e2915a | 2011-03-20 | Marcus Comstedt | |
string shortref = ref_name;
sscanf(shortref, "refs/%*[^/]/%s", shortref);
if (has_prefix(shortref, "scratch/") || has_prefix(shortref, user+"/"))
return 2;
if (search(ref_name, "/x-") >= 0) {
write("The ref %s can only be modified by its owner\n", ref_name);
return 0;
}
return 1;
}
int check_tag_push(string old_sha, string new_sha, string ref_name,
int access_level)
{
if (access_level >= 2)
return 0;
|
eecd8a | 2010-09-27 | Marcus Comstedt | | string oldtag =
String.trim_all_whites(run_git_ex(1, "rev-parse", "--verify",
"-q", ref_name));
if (sizeof(oldtag) && oldtag != new_sha) {
|
e2915a | 2011-03-20 | Marcus Comstedt | | write("Tag %s already exists with value %s, will not %s it\n",
ref_name, oldtag, (new_sha == "0"*40? "delete":"move"));
return 1;
}
if (!sizeof(oldtag) && search(ref_name[10..], "/") >= 0) {
write("Common tags are not allowed to contain /.\n");
|
eecd8a | 2010-09-27 | Marcus Comstedt | | return 1;
}
return 0;
}
|
e2915a | 2011-03-20 | Marcus Comstedt | | int check_branch_push(string old_sha, string new_sha, string ref_name,
int access_level)
|
eecd8a | 2010-09-27 | Marcus Comstedt | | {
if (old_sha == "0"*40) {
|
e2915a | 2011-03-20 | Marcus Comstedt | |
if (sscanf(ref_name, "refs/heads/%*[0-9.]%*c") < 2) {
write("Main version branches can not be created remotely.\n");
return 1;
}
if (access_level < 2 && search(ref_name[11..], "/")>=0) {
write("Common topic branch names are not allowed to contain /.\n");
return 1;
}
return 0;
} else if (new_sha == "0"*40) {
if (access_level < 2) {
write("You may not delete branches which do not belong to you.\n");
return 1;
}
|
efde06 | 2010-09-25 | Marcus Comstedt | | return 0;
} else {
|
e2915a | 2011-03-20 | Marcus Comstedt | | if (access_level >= 2)
return 0;
|
eecd8a | 2010-09-27 | Marcus Comstedt | | string merge_base =
String.trim_all_whites(run_git("merge-base", old_sha, new_sha));
if (merge_base != old_sha) {
write("Push to %s is not fast-forward.\n", ref_name);
return 1;
}
|
18ca84 | 2013-01-23 | Marcus Comstedt | | array(string) old_depth = split_lf(run_git("rev-list", "--first-parent",
"-n", "2", old_sha));
|
603496 | 2011-01-13 | Marcus Comstedt | | array(string) fp_path = split_lf(run_git("rev-list", "--first-parent",
|
18ca84 | 2013-01-23 | Marcus Comstedt | | (sizeof(old_depth)<2?
new_sha :
old_sha+"^.."+new_sha)));
|
603496 | 2011-01-13 | Marcus Comstedt | | if (search(fp_path, old_sha)<0) {
write("Commit %s does not contain %s in its first-parent ancestry.\nDid you pull with merge instead of rebase?\n", new_sha, old_sha);
return 1;
}
|
eecd8a | 2010-09-27 | Marcus Comstedt | |
|
4594fa | 2010-10-04 | Marcus Comstedt | | commits_to_check += split_lf(run_git("rev-list", old_sha+".."+new_sha));
|
efde06 | 2010-09-25 | Marcus Comstedt | | return 0;
}
}
|
eecd8a | 2010-09-27 | Marcus Comstedt | | int check_push(string old_sha, string new_sha, string ref_name)
{
|
e2915a | 2011-03-20 | Marcus Comstedt | | string git_user = getenv("GIT_USER")||getenv("USER")||"nobody";
int access_level;
if(!(access_level = check_access(ref_name, git_user))) return 1;
|
eecd8a | 2010-09-27 | Marcus Comstedt | | if (has_prefix(ref_name, "refs/tags/")) {
|
e2915a | 2011-03-20 | Marcus Comstedt | | return check_tag_push(old_sha, new_sha, ref_name, access_level);
|
eecd8a | 2010-09-27 | Marcus Comstedt | | } else if (has_prefix(ref_name, "refs/heads/")) {
|
e2915a | 2011-03-20 | Marcus Comstedt | | return check_branch_push(old_sha, new_sha, ref_name, access_level);
|
eecd8a | 2010-09-27 | Marcus Comstedt | | } else {
write("Trying to push a ref which is neither under refs/tags/ or refs/heads/...\n");
return 1;
}
}
|
efde06 | 2010-09-25 | Marcus Comstedt | | int hook()
{
|
bb7262 | 2010-09-26 | Marcus Comstedt | | foreach(split_lf(Stdio.stdin->read()), string line) {
array(string) args = line / " ";
if(sizeof(args) != 3)
fail("Unexpected input line to pre-receive hook: %s\n", line);
if(check_push(@args))
return 1;
}
|
4594fa | 2010-10-04 | Marcus Comstedt | | foreach(Array.uniq(commits_to_check), string sha)
if(check_commit(sha))
return 1;
|
efde06 | 2010-09-25 | Marcus Comstedt | | return 0;
}
}
|
fa4f8f | 2010-09-27 | Marcus Comstedt | |
class PostCommitHook
{
|
c0b2b3 | 2010-10-04 | Marcus Comstedt | | inherit CommitHookUtilsRepo;
|
fa4f8f | 2010-09-27 | Marcus Comstedt | | void cleanup(string filename, mapping(string:string|int) attr)
{
if(attr->ident && attr->ident != GitAttributes.ATTR_FALSE &&
attr->ident != GitAttributes.ATTR_UNSET &&
search(get_committed_file("HEAD", filename, 1), unexpanded_id)>=0)
if(sizeof(run_git("diff", "--name-only", "--", filename))) {
write("NOTICE: The file %s has a stale ident,\n but I won't touch it since you have unstaged changes.\n", filename);
} else {
run_git("checkout", "HEAD", "--", filename);
}
}
int hook()
{
|
c0b2b3 | 2010-10-04 | Marcus Comstedt | | if (check_commit("HEAD"))
write("NOTICE: Your commit has errors, see above messages. Please amend before push.\n");
foreach(files_to_commit, string filename)
|
fa4f8f | 2010-09-27 | Marcus Comstedt | | cleanup(filename, attrs->checkattr(filename));
|
e6367f | 2010-10-02 | Marcus Comstedt | | string ts_test = check_commit_timestamps("HEAD");
if (ts_test) {
write("NOTICE: Your commit has invalid timestamps: %s\n", ts_test);
write("Please amend it before pushing.\n");
}
|
bdb978 | 2010-10-03 | Marcus Comstedt | | string cm_test = check_commit_msg("HEAD");
if (cm_test) {
write("NOTICE: Your commit message has an encoding problem:\n%s", cm_test);
write("Please ament it before pushing.\n");
}
|
fa4f8f | 2010-09-27 | Marcus Comstedt | | return 0;
}
}
|
c21d88 | 2010-10-04 | Marcus Comstedt | | class PostRewriteHook
{
inherit CommitHookUtilsRepo;
int hook(string command)
{
if (command == "rebase") {
int errs = 0;
foreach(split_lf(Stdio.stdin->read()), string line) {
string old_sha, new_sha, extra;
if(sscanf(line, "%s %s%*[ ]%s", old_sha, new_sha, extra) != 4)
fail("Unparsable input line %O!\n", line);
errs += check_commit(new_sha);
}
if (errs)
write("NOTICE: %d of the commits contain errors. Please amend before pushing.\n", errs);
}
return 0;
}
}
|
59cd49 | 2010-09-05 | Marcus Comstedt | |
|
517918 | 2010-09-25 | Marcus Comstedt | |
|
59cd49 | 2010-09-05 | Marcus Comstedt | | class NiceIdentFilter
{
|
80f91d | 2010-09-05 | Marcus Comstedt | | static string replace_id(string f, function(string:string) replace) {
int p=0;
|
c52896 | 2010-09-26 | Marcus Comstedt | | while((p=search(f, DOLLAR"Id", p)) >= 0) {
int p2 = search(f, DOLLAR, p+3), p3 = search(f, "\n", p+3);
|
4409b4 | 2011-04-25 | Martin Stjernholm | | if (p2 > p && (p3 < 0 || p2 < p3)) {
|
80f91d | 2010-09-05 | Marcus Comstedt | | string r = replace(f[p..p2]);
if (r) {
|
594051 | 2010-09-05 | Marcus Comstedt | |
|
80f91d | 2010-09-05 | Marcus Comstedt | | f = f[..p-1]+r+f[p2+1..];
p += sizeof(r);
|
594051 | 2010-09-05 | Marcus Comstedt | | } else {
p = p2+1;
}
|
80f91d | 2010-09-05 | Marcus Comstedt | | } else p += 3;
}
return f;
}
static string clean_ident(string i)
{
|
c52896 | 2010-09-26 | Marcus Comstedt | | if(has_prefix(i, DOLLAR"Id:") && sizeof(i/" ")==13)
return unexpanded_id;
|
80f91d | 2010-09-05 | Marcus Comstedt | | }
static string smudge_ident(string i)
{
|
c52896 | 2010-09-26 | Marcus Comstedt | | return DOLLAR"Id: some nice ident perhaps, but based on what? "DOLLAR;
|
80f91d | 2010-09-05 | Marcus Comstedt | | }
int clean()
|
59cd49 | 2010-09-05 | Marcus Comstedt | | {
|
80f91d | 2010-09-05 | Marcus Comstedt | | write(replace_id(Stdio.stdin->read(), clean_ident));
return 0;
|
59cd49 | 2010-09-05 | Marcus Comstedt | | }
|
c1f54a | 2010-09-05 | Marcus Comstedt | | int smudge()
|
59cd49 | 2010-09-05 | Marcus Comstedt | | {
|
80f91d | 2010-09-05 | Marcus Comstedt | | write(replace_id(Stdio.stdin->read(), smudge_ident));
return 0;
|
59cd49 | 2010-09-05 | Marcus Comstedt | | }
}
class GitHelper
{
void setup_hooks()
{
constant hooksdir = "hooks";
if (!sizeof(hooks))
return;
if (!file_stat(hooksdir)) {
write("Creating the hooks directory\n");
|
52d815 | 2010-09-05 | Marcus Comstedt | | if (!mkdir(hooksdir))
|
59cd49 | 2010-09-05 | Marcus Comstedt | | iofail("Failed to create %s", hooksdir);
}
foreach (hooks; string name; ) {
string path = combine_path(hooksdir, name);
Stdio.Stat s = file_stat(path, 1);
if (!s) {
write("Installing %s\n", path);
System.symlink(__FILE__, path);
|
52d815 | 2010-09-05 | Marcus Comstedt | | } else if (s->islnk) {
|
59cd49 | 2010-09-05 | Marcus Comstedt | |
} else {
|
517918 | 2010-09-25 | Marcus Comstedt | | write("Hook %s already exists, so won't overwrite it...\n", name);
|
59cd49 | 2010-09-05 | Marcus Comstedt | | }
}
}
void setup_filter(string name, string op)
{
string confname = "filter."+name+"."+op;
string old = String.trim_all_whites(run_git_ex(1, "config", "--get", confname));
string cmd = __FILE__+" filter_"+name+"_"+op;
|
52d815 | 2010-09-05 | Marcus Comstedt | | if (old == "") {
|
59cd49 | 2010-09-05 | Marcus Comstedt | | write("Installing filter operation %s\n", confname);
run_git("config", confname, cmd);
} else if(old == cmd) {
} else {
write("Filter operation %s is already set to %s, not modifying\n",
confname, old);
}
}
void setup_filters()
{
foreach (filters; string name; program fprog) {
object filter = fprog();
foreach (filterops; ; string op)
|
52d815 | 2010-09-05 | Marcus Comstedt | | if (filter[op])
|
59cd49 | 2010-09-05 | Marcus Comstedt | | setup_filter(name, op);
}
}
int setup(array(string) args)
{
if (sizeof(args)) {
werror("githelper.pike should be invoked without arguments...\n");
return 1;
}
if (!cd(String.trim_all_whites(run_git("rev-parse", "--git-dir"))))
iofail("Failed to cd to .git directory");
setup_hooks();
setup_filters();
return 0;
}
}
string get_filter_op(string arg)
{
if (!has_prefix(arg, "filter_"))
return 0;
foreach (filterops; ; string op)
if (has_suffix(arg, "_"+op))
return op;
|
070acd | 2010-09-05 | Marcus Comstedt | | return 0;
|
59cd49 | 2010-09-05 | Marcus Comstedt | | }
int main(int argc, array(string) argv)
{
string command_name = basename(argv[0]);
if (hooks[command_name])
return hooks[command_name]()->hook(@argv[1..]);
else if (command_name == "githelper.pike") {
string fop;
if (argc>1 && (fop = get_filter_op(argv[1]))) {
string filter = argv[1][7..sizeof(argv[1])-(sizeof(fop)+2)];
if (filters[filter]) {
object f = filters[filter]();
if (!f[fop]) {
|
52d815 | 2010-09-05 | Marcus Comstedt | | werror("Filter %s does not implement %s!\n", filter, fop);
return 1;
|
59cd49 | 2010-09-05 | Marcus Comstedt | | } else
return f[fop](@argv[2..]);
} else {
werror("Unknown filter %s!\n", filter);
return 1;
}
} else
return GitHelper()->setup(argv[1..]);
} else {
werror("Unknown invocation method %s!\n", command_name);
return 1;
}
}
|