Skip to content

Commit

Permalink
fsck: git receive-pack: support excluding objects from fsck'ing
Browse files Browse the repository at this point in the history
The optional new config option `receive.fsck.skipList` specifies the path
to a file listing the names, i.e. SHA-1s, one per line, of objects that
are to be ignored by `git receive-pack` when `receive.fsckObjects = true`.

This is extremely handy in case of legacy repositories where it would
cause more pain to change incorrect objects than to live with them
(e.g. a duplicate 'author' line in an early commit object).

The intended use case is for server administrators to inspect objects
that are reported by `git push` as being too problematic to enter the
repository, and to add the objects' SHA-1 to a (preferably sorted) file
when the objects are legitimate, i.e. when it is determined that those
problematic objects should be allowed to enter the server.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Johannes Schindelin authored and Junio C Hamano committed Jun 23, 2015
1 parent 02976bf commit cd94c6f
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2222,6 +2222,14 @@ which would not pass pushing when `receive.fsckObjects = true`, allowing
the host to accept repositories with certain known issues but still catch
other issues.

receive.fsck.skipList::
The path to a sorted list of object names (i.e. one SHA-1 per
line) that are known to be broken in a non-fatal way and should
be ignored. This feature is useful when an established project
should be accepted despite early commits containing errors that
can be safely ignored such as invalid committer email addresses.
Note: corrupt objects cannot be skipped with this setting.

receive.unpackLimit::
If the number of objects received in a push is below this
limit then the objects will be unpacked into loose object
Expand Down
11 changes: 11 additions & 0 deletions builtin/receive-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
return 0;
}

if (strcmp(var, "receive.fsck.skiplist") == 0) {
const char *path;

if (git_config_pathname(&path, var, value))
return 1;
strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
fsck_msg_types.len ? ',' : '=', path);
free((char *)path);
return 0;
}

if (skip_prefix(var, "receive.fsck.", &var)) {
if (is_valid_msg_type(var, value))
strbuf_addf(&fsck_msg_types, "%c%s=%s",
Expand Down
50 changes: 50 additions & 0 deletions fsck.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "fsck.h"
#include "refs.h"
#include "utf8.h"
#include "sha1-array.h"

#define FSCK_FATAL -1
#define FSCK_INFO -2
Expand Down Expand Up @@ -127,6 +128,43 @@ static int fsck_msg_type(enum fsck_msg_id msg_id,
return msg_type;
}

static void init_skiplist(struct fsck_options *options, const char *path)
{
static struct sha1_array skiplist = SHA1_ARRAY_INIT;
int sorted, fd;
char buffer[41];
unsigned char sha1[20];

if (options->skiplist)
sorted = options->skiplist->sorted;
else {
sorted = 1;
options->skiplist = &skiplist;
}

fd = open(path, O_RDONLY);
if (fd < 0)
die("Could not open skip list: %s", path);
for (;;) {
int result = read_in_full(fd, buffer, sizeof(buffer));
if (result < 0)
die_errno("Could not read '%s'", path);
if (!result)
break;
if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n')
die("Invalid SHA-1: %s", buffer);
sha1_array_append(&skiplist, sha1);
if (sorted && skiplist.nr > 1 &&
hashcmp(skiplist.sha1[skiplist.nr - 2],
sha1) > 0)
sorted = 0;
}
close(fd);

if (sorted)
skiplist.sorted = 1;
}

static int parse_msg_type(const char *str)
{
if (!strcmp(str, "error"))
Expand Down Expand Up @@ -191,6 +229,14 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)
buf[equal] = tolower(buf[equal]);
buf[equal] = '\0';

if (!strcmp(buf, "skiplist")) {
if (equal == len)
die("skiplist requires a path");
init_skiplist(options, buf + equal + 1);
buf += len + 1;
continue;
}

if (equal == len)
die("Missing '=': '%s'", buf);

Expand Down Expand Up @@ -229,6 +275,10 @@ static int report(struct fsck_options *options, struct object *object,
if (msg_type == FSCK_IGNORE)
return 0;

if (options->skiplist && object &&
sha1_array_lookup(options->skiplist, object->sha1) >= 0)
return 0;

if (msg_type == FSCK_FATAL)
msg_type = FSCK_ERROR;
else if (msg_type == FSCK_INFO)
Expand Down
1 change: 1 addition & 0 deletions fsck.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ struct fsck_options {
fsck_error error_func;
unsigned strict:1;
int *msg_type;
struct sha1_array *skiplist;
};

#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }
Expand Down
12 changes: 12 additions & 0 deletions t/t5504-fetch-receive-strict.sh
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,18 @@ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000
This commit object intentionally broken
EOF

test_expect_success 'push with receive.fsck.skipList' '
commit="$(git hash-object -t commit -w --stdin <bogus-commit)" &&
git push . $commit:refs/heads/bogus &&
rm -rf dst &&
git init dst &&
git --git-dir=dst/.git config receive.fsckObjects true &&
test_must_fail git push --porcelain dst bogus &&
git --git-dir=dst/.git config receive.fsck.skipList SKIP &&
echo $commit >dst/.git/SKIP &&
git push --porcelain dst bogus
'

test_expect_success 'push with receive.fsck.missingEmail=warn' '
commit="$(git hash-object -t commit -w --stdin <bogus-commit)" &&
git push . $commit:refs/heads/bogus &&
Expand Down

0 comments on commit cd94c6f

Please sign in to comment.