Skip to content

Commit ea25a18

Browse files
convert: add "status=delayed" to filter process protocol
Some `clean` / `smudge` filters might require a significant amount of time to process a single blob. During this process the Git checkout operation is blocked and Git needs to wait until the filter is done to continue with the checkout. Teach the filter process protocol (introduced in edcc858) to accept the status "delayed" as response to a filter request. Upon this response Git continues with the checkout operation and asks the filter to process the blob again after all other blobs have been processed. Git has a multiple code paths that checkout a blob. Support delayed checkouts only in `clone` (in unpack-trees.c) and `checkout` operations. Signed-off-by: Lars Schneider <larsxschneider@gmail.com>
1 parent e05806d commit ea25a18

File tree

14 files changed

+145
-24
lines changed

14 files changed

+145
-24
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
------------------------

apply.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4328,7 +4328,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
43284328
if (fd < 0)
43294329
return 1;
43304330

4331-
if (convert_to_working_tree(path, buf, size, &nbuf)) {
4331+
if (convert_to_working_tree(path, buf, size, &nbuf, NULL)) {
43324332
size = nbuf.len;
43334333
buf = nbuf.buf;
43344334
}

archive.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ void *sha1_file_to_archive(const struct archiver_args *args,
7777
size_t size = 0;
7878

7979
strbuf_attach(&buf, buffer, *sizep, *sizep + 1);
80-
convert_to_working_tree(path, buf.buf, buf.len, &buf);
80+
convert_to_working_tree(path, buf.buf, buf.len, &buf, NULL);
8181
if (commit)
8282
format_subst(commit, buf.buf, buf.len, &buf);
8383
buffer = strbuf_detach(&buf, &size);

builtin/cat-file.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ static int filter_object(const char *path, unsigned mode,
3535
oid_to_hex(oid), path);
3636
if ((type == OBJ_BLOB) && S_ISREG(mode)) {
3737
struct strbuf strbuf = STRBUF_INIT;
38-
if (convert_to_working_tree(path, *buf, *size, &strbuf)) {
38+
if (convert_to_working_tree(path, *buf, *size, &strbuf, NULL)) {
3939
free(*buf);
4040
*size = strbuf.len;
4141
*buf = strbuf_detach(&strbuf, NULL);

builtin/checkout.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ static int checkout_paths(const struct checkout_opts *opts,
369369
pos = skip_same_name(ce, pos) - 1;
370370
}
371371
}
372+
errs |= checkout_delayed_entries(&state);
372373

373374
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
374375
die(_("unable to write new index file"));

cache.h

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

13761376
#define TEMPORARY_FILENAME_LENGTH 25
13771377
extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
1378+
extern int checkout_delayed_entries(const struct checkout *state);
13781379

13791380
struct cache_def {
13801381
struct strbuf path;

convert.c

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,7 @@ static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, cons
672672
}
673673

674674
static int apply_multi_file_filter(const char *path, const char *src, size_t len,
675-
int fd, struct strbuf *dst, const char *cmd,
675+
int fd, struct strbuf *dst, int *delayed, const char *cmd,
676676
const unsigned int wanted_capability)
677677
{
678678
int err;
@@ -738,9 +738,14 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
738738
goto done;
739739

740740
read_multi_file_filter_status(process->out, &filter_status);
741-
err = strcmp(filter_status.buf, "success");
742-
if (err)
741+
if (delayed && !strcmp(filter_status.buf, "delayed")) {
742+
*delayed = 1;
743743
goto done;
744+
} else {
745+
err = strcmp(filter_status.buf, "success");
746+
if (err)
747+
goto done;
748+
}
744749

745750
err = read_packetized_to_strbuf(process->out, &nbuf) < 0;
746751
if (err)
@@ -787,8 +792,8 @@ static struct convert_driver {
787792
} *user_convert, **user_convert_tail;
788793

789794
static int apply_filter(const char *path, const char *src, size_t len,
790-
int fd, struct strbuf *dst, struct convert_driver *drv,
791-
const unsigned int wanted_capability)
795+
int fd, struct strbuf *dst, int *delayed,
796+
struct convert_driver *drv, const unsigned int wanted_capability)
792797
{
793798
const char *cmd = NULL;
794799

@@ -806,7 +811,7 @@ static int apply_filter(const char *path, const char *src, size_t len,
806811
if (cmd && *cmd)
807812
return apply_single_file_filter(path, src, len, fd, dst, cmd);
808813
else if (drv->process && *drv->process)
809-
return apply_multi_file_filter(path, src, len, fd, dst, drv->process, wanted_capability);
814+
return apply_multi_file_filter(path, src, len, fd, dst, delayed, drv->process, wanted_capability);
810815

811816
return 0;
812817
}
@@ -1152,7 +1157,7 @@ int would_convert_to_git_filter_fd(const char *path)
11521157
if (!ca.drv->required)
11531158
return 0;
11541159

1155-
return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN);
1160+
return apply_filter(path, NULL, 0, -1, NULL, NULL, ca.drv, CAP_CLEAN);
11561161
}
11571162

11581163
const char *get_convert_attr_ascii(const char *path)
@@ -1189,7 +1194,7 @@ int convert_to_git(const char *path, const char *src, size_t len,
11891194

11901195
convert_attrs(&ca, path);
11911196

1192-
ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN);
1197+
ret |= apply_filter(path, src, len, -1, dst, NULL, ca.drv, CAP_CLEAN);
11931198
if (!ret && ca.drv && ca.drv->required)
11941199
die("%s: clean filter '%s' failed", path, ca.drv->name);
11951200

@@ -1214,15 +1219,15 @@ void convert_to_git_filter_fd(const char *path, int fd, struct strbuf *dst,
12141219
assert(ca.drv);
12151220
assert(ca.drv->clean || ca.drv->process);
12161221

1217-
if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN))
1222+
if (!apply_filter(path, NULL, 0, fd, dst, NULL, ca.drv, CAP_CLEAN))
12181223
die("%s: clean filter '%s' failed", path, ca.drv->name);
12191224

12201225
crlf_to_git(path, dst->buf, dst->len, dst, ca.crlf_action, checksafe);
12211226
ident_to_git(path, dst->buf, dst->len, dst, ca.ident);
12221227
}
12231228

12241229
static int convert_to_working_tree_internal(const char *path, const char *src,
1225-
size_t len, struct strbuf *dst,
1230+
size_t len, struct strbuf *dst, int *delayed,
12261231
int normalizing)
12271232
{
12281233
int ret = 0, ret_filter = 0;
@@ -1248,21 +1253,21 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
12481253
}
12491254
}
12501255

1251-
ret_filter = apply_filter(path, src, len, -1, dst, ca.drv, CAP_SMUDGE);
1256+
ret_filter = apply_filter(path, src, len, -1, dst, delayed, ca.drv, CAP_SMUDGE);
12521257
if (!ret_filter && ca.drv && ca.drv->required)
12531258
die("%s: smudge filter %s failed", path, ca.drv->name);
12541259

12551260
return ret | ret_filter;
12561261
}
12571262

1258-
int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
1263+
int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst, int *delayed)
12591264
{
1260-
return convert_to_working_tree_internal(path, src, len, dst, 0);
1265+
return convert_to_working_tree_internal(path, src, len, dst, delayed, 0);
12611266
}
12621267

12631268
int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst)
12641269
{
1265-
int ret = convert_to_working_tree_internal(path, src, len, dst, 1);
1270+
int ret = convert_to_working_tree_internal(path, src, len, dst, NULL, 1);
12661271
if (ret) {
12671272
src = dst->buf;
12681273
len = dst->len;

convert.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ extern const char *get_convert_attr_ascii(const char *path);
4141
extern int convert_to_git(const char *path, const char *src, size_t len,
4242
struct strbuf *dst, enum safe_crlf checksafe);
4343
extern int convert_to_working_tree(const char *path, const char *src,
44-
size_t len, struct strbuf *dst);
44+
size_t len, struct strbuf *dst, int *delayed);
4545
extern int renormalize_buffer(const char *path, const char *src, size_t len,
4646
struct strbuf *dst);
4747
static inline int would_convert_to_git(const char *path)

diff.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2960,7 +2960,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
29602960
if (fd < 0)
29612961
die_errno("unable to create temp-file");
29622962
if (convert_to_working_tree(path,
2963-
(const char *)blob, (size_t)size, &buf)) {
2963+
(const char *)blob, (size_t)size, &buf, NULL)) {
29642964
blob = buf.buf;
29652965
size = buf.len;
29662966
}

entry.c

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
#include "blob.h"
33
#include "dir.h"
44
#include "streaming.h"
5+
#include "list.h"
6+
7+
static LIST_HEAD(delayed_cache_entry_queue_head);
8+
9+
struct delayed_cache_entry {
10+
struct cache_entry *ce;
11+
struct list_head node;
12+
};
513

614
static void create_directories(const char *path, int path_len,
715
const struct checkout *state)
@@ -140,12 +148,13 @@ static int write_entry(struct cache_entry *ce,
140148
char *path, const struct checkout *state, int to_tempfile)
141149
{
142150
unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
143-
int fd, ret, fstat_done = 0;
151+
int fd, ret, fstat_done = 0, delayed = 0;
144152
char *new;
145153
struct strbuf buf = STRBUF_INIT;
146154
unsigned long size;
147155
size_t wrote, newsize = 0;
148156
struct stat st;
157+
struct delayed_cache_entry *delayed_ce;
149158

150159
if (ce_mode_s_ifmt == S_IFREG) {
151160
struct stream_filter *filter = get_stream_filter(ce->name,
@@ -178,10 +187,17 @@ static int write_entry(struct cache_entry *ce,
178187
* Convert from git internal format to working tree format
179188
*/
180189
if (ce_mode_s_ifmt == S_IFREG &&
181-
convert_to_working_tree(ce->name, new, size, &buf)) {
190+
convert_to_working_tree(ce->name, new, size, &buf, &delayed)) {
182191
free(new);
183-
new = strbuf_detach(&buf, &newsize);
184-
size = newsize;
192+
if (delayed) {
193+
delayed_ce = xmalloc(sizeof(*delayed_ce));
194+
delayed_ce->ce = ce;
195+
list_add_tail(&delayed_ce->node, &delayed_cache_entry_queue_head);
196+
goto finish;
197+
} else {
198+
new = strbuf_detach(&buf, &newsize);
199+
size = newsize;
200+
}
185201
}
186202

187203
fd = open_output_fd(path, ce, to_tempfile);
@@ -291,3 +307,19 @@ int checkout_entry(struct cache_entry *ce,
291307
create_directories(path.buf, path.len, state);
292308
return write_entry(ce, path.buf, state, 0);
293309
}
310+
311+
int checkout_delayed_entries(const struct checkout *state)
312+
{
313+
struct delayed_cache_entry *head;
314+
int errs = 0;
315+
316+
while (!list_empty(&delayed_cache_entry_queue_head)) {
317+
head = list_first_entry(&delayed_cache_entry_queue_head,
318+
struct delayed_cache_entry, node);
319+
list_del(&head->node);
320+
head->ce->ce_flags &= ~CE_UPDATE;
321+
errs |= checkout_entry(head->ce, state, NULL);
322+
}
323+
324+
return errs;
325+
}

0 commit comments

Comments
 (0)