Skip to content

Commit 51c5f68

Browse files
committed
Merge branch 'ls/filter-process-delayed' into pu
What's the status of this one??? cf. <[email protected]> * ls/filter-process-delayed: convert: add "status=delayed" to filter process protocol
2 parents 4d86198 + 9ca6189 commit 51c5f68

File tree

9 files changed

+173
-16
lines changed

9 files changed

+173
-16
lines changed

Documentation/gitattributes.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,15 @@ packet: git< 0000 # empty content!
473473
packet: git< 0000 # empty list, keep "status=success" unchanged!
474474
------------------------
475475

476+
If the request cannot be fulfilled within a reasonable amount of time
477+
then the filter can respond with a "delayed" status and a flush packet.
478+
Git will perform the same request at a later point in time, again. The
479+
filter can delay a response multiple times for a single request.
480+
------------------------
481+
packet: git< status=delayed
482+
packet: git< 0000
483+
------------------------
484+
476485
In case the filter cannot or does not want to process the content,
477486
it is expected to respond with an "error" status.
478487
------------------------

builtin/checkout.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ static int checkout_paths(const struct checkout_opts *opts,
392392
pos = skip_same_name(ce, pos) - 1;
393393
}
394394
}
395+
errs |= checkout_delayed_entries(&state);
395396

396397
if (opts->no_index)
397398
; /* discard the in-core index */

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,6 +1552,7 @@ struct checkout {
15521552

15531553
#define TEMPORARY_FILENAME_LENGTH 25
15541554
extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
1555+
extern int checkout_delayed_entries(const struct checkout *state);
15551556

15561557
struct cache_def {
15571558
struct strbuf path;

convert.c

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "quote.h"
55
#include "sigchain.h"
66
#include "pkt-line.h"
7+
#include "list.h"
78
#include "sub-process.h"
89

910
/*
@@ -39,6 +40,13 @@ struct text_stat {
3940
unsigned printable, nonprintable;
4041
};
4142

43+
static LIST_HEAD(delayed_item_queue_head);
44+
45+
struct delayed_item {
46+
void* item;
47+
struct list_head node;
48+
};
49+
4250
static void gather_stats(const char *buf, unsigned long size, struct text_stat *stats)
4351
{
4452
unsigned long i;
@@ -566,7 +574,7 @@ static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
566574
}
567575

568576
static int apply_multi_file_filter(const char *path, const char *src, size_t len,
569-
int fd, struct strbuf *dst, const char *cmd,
577+
int fd, struct strbuf *dst, int *delayed, const char *cmd,
570578
const unsigned int wanted_capability)
571579
{
572580
int err;
@@ -639,6 +647,10 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
639647
if (err)
640648
goto done;
641649

650+
if (delayed && !strcmp(filter_status.buf, "delayed")) {
651+
*delayed = 1;
652+
goto done;
653+
}
642654
err = strcmp(filter_status.buf, "success");
643655
if (err)
644656
goto done;
@@ -692,8 +704,8 @@ static struct convert_driver {
692704
} *user_convert, **user_convert_tail;
693705

694706
static int apply_filter(const char *path, const char *src, size_t len,
695-
int fd, struct strbuf *dst, struct convert_driver *drv,
696-
const unsigned int wanted_capability)
707+
int fd, struct strbuf *dst, int *delayed,
708+
struct convert_driver *drv, const unsigned int wanted_capability)
697709
{
698710
const char *cmd = NULL;
699711

@@ -711,7 +723,7 @@ static int apply_filter(const char *path, const char *src, size_t len,
711723
if (cmd && *cmd)
712724
return apply_single_file_filter(path, src, len, fd, dst, cmd);
713725
else if (drv->process && *drv->process)
714-
return apply_multi_file_filter(path, src, len, fd, dst, drv->process, wanted_capability);
726+
return apply_multi_file_filter(path, src, len, fd, dst, delayed, drv->process, wanted_capability);
715727

716728
return 0;
717729
}
@@ -1052,7 +1064,7 @@ int would_convert_to_git_filter_fd(const char *path)
10521064
if (!ca.drv->required)
10531065
return 0;
10541066

1055-
return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN);
1067+
return apply_filter(path, NULL, 0, -1, NULL, NULL, ca.drv, CAP_CLEAN);
10561068
}
10571069

10581070
const char *get_convert_attr_ascii(const char *path)
@@ -1089,7 +1101,7 @@ int convert_to_git(const char *path, const char *src, size_t len,
10891101

10901102
convert_attrs(&ca, path);
10911103

1092-
ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN);
1104+
ret |= apply_filter(path, src, len, -1, dst, NULL, ca.drv, CAP_CLEAN);
10931105
if (!ret && ca.drv && ca.drv->required)
10941106
die("%s: clean filter '%s' failed", path, ca.drv->name);
10951107

@@ -1114,15 +1126,15 @@ void convert_to_git_filter_fd(const char *path, int fd, struct strbuf *dst,
11141126
assert(ca.drv);
11151127
assert(ca.drv->clean || ca.drv->process);
11161128

1117-
if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN))
1129+
if (!apply_filter(path, NULL, 0, fd, dst, NULL, ca.drv, CAP_CLEAN))
11181130
die("%s: clean filter '%s' failed", path, ca.drv->name);
11191131

11201132
crlf_to_git(path, dst->buf, dst->len, dst, ca.crlf_action, checksafe);
11211133
ident_to_git(path, dst->buf, dst->len, dst, ca.ident);
11221134
}
11231135

11241136
static int convert_to_working_tree_internal(const char *path, const char *src,
1125-
size_t len, struct strbuf *dst,
1137+
size_t len, struct strbuf *dst, int *delayed,
11261138
int normalizing)
11271139
{
11281140
int ret = 0, ret_filter = 0;
@@ -1148,21 +1160,50 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
11481160
}
11491161
}
11501162

1151-
ret_filter = apply_filter(path, src, len, -1, dst, ca.drv, CAP_SMUDGE);
1163+
ret_filter = apply_filter(path, src, len, -1, dst, delayed, ca.drv, CAP_SMUDGE);
11521164
if (!ret_filter && ca.drv && ca.drv->required)
11531165
die("%s: smudge filter %s failed", path, ca.drv->name);
11541166

11551167
return ret | ret_filter;
11561168
}
11571169

1170+
int async_convert_to_working_tree(const char *path, const char *src,
1171+
size_t len, struct strbuf *dst, void *item)
1172+
{
1173+
int delayed = 0;
1174+
struct delayed_item *delayed_item;
1175+
if (convert_to_working_tree_internal(path, src, len, dst, &delayed, 0)) {
1176+
if (delayed) {
1177+
delayed_item = xmalloc(sizeof(*delayed_item));
1178+
delayed_item->item = item;
1179+
list_add_tail(&delayed_item->node, &delayed_item_queue_head);
1180+
return ASYNC_FILTER_DELAYED;
1181+
}
1182+
return ASYNC_FILTER_SUCCESS;
1183+
}
1184+
return ASYNC_FILTER_FAIL;
1185+
}
1186+
1187+
void* async_filter_finish(void)
1188+
{
1189+
struct delayed_item *head;
1190+
if (!list_empty(&delayed_item_queue_head)) {
1191+
head = list_first_entry(&delayed_item_queue_head,
1192+
struct delayed_item, node);
1193+
list_del(&head->node);
1194+
return head->item;
1195+
}
1196+
return NULL;
1197+
}
1198+
11581199
int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
11591200
{
1160-
return convert_to_working_tree_internal(path, src, len, dst, 0);
1201+
return convert_to_working_tree_internal(path, src, len, dst, NULL, 0);
11611202
}
11621203

11631204
int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst)
11641205
{
1165-
int ret = convert_to_working_tree_internal(path, src, len, dst, 1);
1206+
int ret = convert_to_working_tree_internal(path, src, len, dst, NULL, 1);
11661207
if (ret) {
11671208
src = dst->buf;
11681209
len = dst->len;

convert.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44
#ifndef CONVERT_H
55
#define CONVERT_H
66

7+
enum async_filter {
8+
ASYNC_FILTER_SUCCESS = 0,
9+
ASYNC_FILTER_FAIL = 1,
10+
ASYNC_FILTER_DELAYED = 2
11+
};
12+
13+
extern enum async_filter async_filter;
14+
15+
716
enum safe_crlf {
817
SAFE_CRLF_FALSE = 0,
918
SAFE_CRLF_FAIL = 1,
@@ -42,6 +51,10 @@ extern int convert_to_git(const char *path, const char *src, size_t len,
4251
struct strbuf *dst, enum safe_crlf checksafe);
4352
extern int convert_to_working_tree(const char *path, const char *src,
4453
size_t len, struct strbuf *dst);
54+
extern int async_convert_to_working_tree(const char *path, const char *src,
55+
size_t len, struct strbuf *dst,
56+
void *item);
57+
extern void* async_filter_finish(void);
4558
extern int renormalize_buffer(const char *path, const char *src, size_t len,
4659
struct strbuf *dst);
4760
static inline int would_convert_to_git(const char *path)

entry.c

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,17 @@ static int write_entry(struct cache_entry *ce,
169169
/*
170170
* Convert from git internal format to working tree format
171171
*/
172-
if (ce_mode_s_ifmt == S_IFREG &&
173-
convert_to_working_tree(ce->name, new, size, &buf)) {
174-
free(new);
175-
new = strbuf_detach(&buf, &newsize);
176-
size = newsize;
172+
if (ce_mode_s_ifmt == S_IFREG) {
173+
ret = async_convert_to_working_tree(ce->name, new, size, &buf, ce);
174+
if (ret == ASYNC_FILTER_SUCCESS) {
175+
free(new);
176+
new = strbuf_detach(&buf, &newsize);
177+
size = newsize;
178+
}
179+
else if (ret == ASYNC_FILTER_DELAYED) {
180+
free(new);
181+
goto finish;
182+
}
177183
}
178184

179185
fd = open_output_fd(path, ce, to_tempfile);
@@ -311,3 +317,16 @@ int checkout_entry(struct cache_entry *ce,
311317
create_directories(path.buf, path.len, state);
312318
return write_entry(ce, path.buf, state, 0);
313319
}
320+
321+
int checkout_delayed_entries(const struct checkout *state)
322+
{
323+
struct cache_entry *ce;
324+
int errs = 0;
325+
326+
while ((ce = async_filter_finish())) {
327+
ce->ce_flags &= ~CE_UPDATE;
328+
errs |= checkout_entry(ce, state, NULL);
329+
}
330+
331+
return errs;
332+
}

t/t0021-conversion.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,4 +701,57 @@ test_expect_success PERL 'invalid process filter must fail (and not hang!)' '
701701
)
702702
'
703703

704+
test_expect_success PERL 'delayed checkout in process filter' '
705+
test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
706+
test_config_global filter.protocol.required true &&
707+
rm -rf repo &&
708+
mkdir repo &&
709+
(
710+
cd repo &&
711+
git init &&
712+
echo "*.r filter=protocol" >.gitattributes &&
713+
cp "$TEST_ROOT/test.o" test.r &&
714+
cp "$TEST_ROOT/test.o" test-delay1.r &&
715+
cp "$TEST_ROOT/test.o" test-delay3.r &&
716+
git add . &&
717+
git commit -m "test commit 1"
718+
) &&
719+
720+
S=$(file_size repo/test.r) &&
721+
rm -rf repo-cloned &&
722+
filter_git clone repo repo-cloned &&
723+
cat >expected.log <<-EOF &&
724+
START
725+
init handshake complete
726+
IN: smudge test.r $S [OK] -- OUT: $S . [OK]
727+
IN: smudge test-delay1.r $S [OK] -- OUT: $S [DELAYED]
728+
IN: smudge test-delay1.r $S [OK] -- OUT: $S . [OK]
729+
IN: smudge test-delay3.r $S [OK] -- OUT: $S [DELAYED]
730+
IN: smudge test-delay3.r $S [OK] -- OUT: $S [DELAYED]
731+
IN: smudge test-delay3.r $S [OK] -- OUT: $S [DELAYED]
732+
IN: smudge test-delay3.r $S [OK] -- OUT: $S . [OK]
733+
STOP
734+
EOF
735+
test_cmp_count expected.log repo-cloned/rot13-filter.log &&
736+
737+
(
738+
cd repo-cloned &&
739+
rm *.r rot13-filter.log &&
740+
filter_git checkout . &&
741+
cat >expected.log <<-EOF &&
742+
START
743+
init handshake complete
744+
IN: smudge test.r $S [OK] -- OUT: $S . [OK]
745+
IN: smudge test-delay1.r $S [OK] -- OUT: $S [DELAYED]
746+
IN: smudge test-delay1.r $S [OK] -- OUT: $S . [OK]
747+
IN: smudge test-delay3.r $S [OK] -- OUT: $S [DELAYED]
748+
IN: smudge test-delay3.r $S [OK] -- OUT: $S [DELAYED]
749+
IN: smudge test-delay3.r $S [OK] -- OUT: $S [DELAYED]
750+
IN: smudge test-delay3.r $S [OK] -- OUT: $S . [OK]
751+
STOP
752+
EOF
753+
test_cmp_count expected.log rot13-filter.log
754+
)
755+
'
756+
704757
test_done

t/t0021/rot13-filter.pl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
# operation then the filter signals that it cannot or does not want
1818
# to process the file and any file after that is processed with the
1919
# same command.
20+
# (5) If data with a pathname that is a key in the DELAY hash is
21+
# processed (e.g. 'test-delay1.r') then the filter signals n times
22+
# to Git that the processing is delayed (n being the value of the
23+
# DELAY hash key).
2024
#
2125

2226
use strict;
@@ -25,6 +29,12 @@
2529

2630
my $MAX_PACKET_CONTENT_SIZE = 65516;
2731
my @capabilities = @ARGV;
32+
my $DELAY3 = 3;
33+
my $DELAY1 = 1;
34+
35+
my %DELAY;
36+
$DELAY{'test-delay1.r'} = 1;
37+
$DELAY{'test-delay3.r'} = 3;
2838

2939
open my $debug, ">>", "rot13-filter.log" or die "cannot open log file: $!";
3040

@@ -166,6 +176,15 @@ sub packet_flush {
166176
packet_txt_write("status=abort");
167177
packet_flush();
168178
}
179+
elsif ( $command eq "smudge" and
180+
exists $DELAY{$pathname} and
181+
$DELAY{$pathname} > 0 ) {
182+
$DELAY{$pathname} = $DELAY{$pathname} - 1;
183+
print $debug "[DELAYED]\n";
184+
$debug->flush();
185+
packet_txt_write("status=delayed");
186+
packet_flush();
187+
}
169188
else {
170189
packet_txt_write("status=success");
171190
packet_flush();

unpack-trees.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ static int check_updates(struct unpack_trees_options *o)
388388
}
389389
}
390390
}
391+
errs |= checkout_delayed_entries(&state);
391392
stop_progress(&progress);
392393
if (o->update)
393394
git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);

0 commit comments

Comments
 (0)