diff options
author | Junio C Hamano <gitster@pobox.com> | 2011-05-12 14:31:08 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2011-05-20 18:46:58 -0700 |
commit | dd8e912190540ef6578386aa1343fbbe196cb8c1 (patch) | |
tree | f66b651a6c060a07a280d0d6aa73cd21c134b1e8 /entry.c | |
parent | 46bf043807cc5d8986f41139a8c28491f613c5e0 (diff) | |
download | git-dd8e912190540ef6578386aa1343fbbe196cb8c1.tar.gz |
streaming_write_entry(): use streaming API in write_entry()
When the output to a path does not have to be converted, we can read from
the object database from the streaming API and write to the file in the
working tree, without having to hold everything in the memory.
The ident, auto- and safe- crlf conversions inherently require you to read
the whole thing before deciding what to do, so while it is technically
possible to support them by using a buffer of an unbound size or rewinding
and reading the stream twice, it is less practical than the traditional
"read the whole thing in core and convert" approach.
Adding streaming filters for the other conversions on top of this should
be doable by tweaking the can_bypass_conversion() function (it should be
renamed to can_filter_stream() when it happens). Then the streaming API
can be extended to wrap the git_istream streaming_write_entry() opens on
the underlying object in another git_istream that reads from it, filters
what is read, and let the streaming_write_entry() read the filtered
result. But that is outside the scope of this series.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'entry.c')
-rw-r--r-- | entry.c | 52 |
1 files changed, 52 insertions, 0 deletions
@@ -1,6 +1,7 @@ #include "cache.h" #include "blob.h" #include "dir.h" +#include "streaming.h" static void create_directories(const char *path, int path_len, const struct checkout *state) @@ -114,6 +115,50 @@ static int fstat_output(int fd, const struct checkout *state, struct stat *st) return 0; } +static int streaming_write_entry(struct cache_entry *ce, char *path, + const struct checkout *state, int to_tempfile, + int *fstat_done, struct stat *statbuf) +{ + struct git_istream *st; + enum object_type type; + unsigned long sz; + int result = -1; + int fd = -1; + + st = open_istream(ce->sha1, &type, &sz); + if (!st) + return -1; + if (type != OBJ_BLOB) + goto close_and_exit; + + fd = open_output_fd(path, ce, to_tempfile); + if (fd < 0) + goto close_and_exit; + + for (;;) { + char buf[10240]; + ssize_t wrote; + ssize_t readlen = read_istream(st, buf, sizeof(buf)); + + if (!readlen) + break; + + wrote = write_in_full(fd, buf, readlen); + + if (wrote != readlen) + goto close_and_exit; + } + *fstat_done = fstat_output(fd, state, statbuf); + +close_and_exit: + close_istream(st); + if (0 <= fd) + result = close(fd); + if (result && 0 <= fd) + unlink(path); + return result; +} + static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile) { unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT; @@ -124,6 +169,12 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout size_t wrote, newsize = 0; struct stat st; + if ((ce_mode_s_ifmt == S_IFREG) && + can_bypass_conversion(path) && + !streaming_write_entry(ce, path, state, to_tempfile, + &fstat_done, &st)) + goto finish; + switch (ce_mode_s_ifmt) { case S_IFREG: case S_IFLNK: @@ -176,6 +227,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout return error("unknown file mode for %s in index", path); } +finish: if (state->refresh_cache) { if (!fstat_done) lstat(ce->name, &st); |