summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/config.txt11
-rw-r--r--cache.h1
-rw-r--r--config.c10
-rw-r--r--environment.c1
-rw-r--r--sha1_file.c66
5 files changed, 76 insertions, 13 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 28fe6942cf..d71653dc65 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -118,6 +118,17 @@ core.legacyheaders::
database directly (where the "http://" and "rsync://" protocols
count as direct access).
+core.packedGitWindowSize::
+ Number of bytes of a pack file to map into memory in a
+ single mapping operation. Larger window sizes may allow
+ your system to process a smaller number of large pack files
+ more quickly. Smaller window sizes will negatively affect
+ performance due to increased calls to the opreating system's
+ memory manager, but may improve performance when accessing
+ a large number of large pack files. Default is 32 MiB,
+ which should be reasonable for all users/operating systems.
+ You probably do not need to adjust this value.
+
core.packedGitLimit::
Maximum number of bytes to map simultaneously into memory
from pack files. If Git needs to access more than this many
diff --git a/cache.h b/cache.h
index c927f29d31..c7d7e4dcc0 100644
--- a/cache.h
+++ b/cache.h
@@ -196,6 +196,7 @@ extern int warn_ambiguous_refs;
extern int shared_repository;
extern const char *apply_default_whitespace;
extern int zlib_compression_level;
+extern size_t packed_git_window_size;
extern size_t packed_git_limit;
#define GIT_REPO_VERSION 0
diff --git a/config.c b/config.c
index 0c21286cb2..21f166e608 100644
--- a/config.c
+++ b/config.c
@@ -298,6 +298,16 @@ int git_default_config(const char *var, const char *value)
return 0;
}
+ if (!strcmp(var, "core.packedgitwindowsize")) {
+ int pgsz = getpagesize();
+ packed_git_window_size = git_config_int(var, value);
+ packed_git_window_size /= pgsz;
+ if (!packed_git_window_size)
+ packed_git_window_size = 1;
+ packed_git_window_size *= pgsz;
+ return 0;
+ }
+
if (!strcmp(var, "core.packedgitlimit")) {
packed_git_limit = git_config_int(var, value);
return 0;
diff --git a/environment.c b/environment.c
index a3ddae68a7..e559fd69c7 100644
--- a/environment.c
+++ b/environment.c
@@ -23,6 +23,7 @@ char *git_log_output_encoding;
int shared_repository = PERM_UMASK;
const char *apply_default_whitespace;
int zlib_compression_level = Z_DEFAULT_COMPRESSION;
+size_t packed_git_window_size = 32 * 1024 * 1024;
size_t packed_git_limit = 256 * 1024 * 1024;
int pager_in_use;
int pager_use_color = 1;
diff --git a/sha1_file.c b/sha1_file.c
index 8e14a5a882..548535c844 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -397,8 +397,9 @@ static char *find_sha1_file(const unsigned char *sha1, struct stat *st)
return NULL;
}
-static int pack_used_ctr;
-static unsigned long pack_mapped;
+static unsigned int pack_used_ctr;
+static size_t pack_mapped;
+static size_t page_size;
struct packed_git *packed_git;
static int check_packed_git_idx(const char *path, unsigned long *idx_size_,
@@ -536,31 +537,70 @@ static void open_packed_git(struct packed_git *p)
die("packfile %s does not match index", p->pack_name);
}
+static int in_window(struct pack_window *win, unsigned long offset)
+{
+ /* We must promise at least 20 bytes (one hash) after the
+ * offset is available from this window, otherwise the offset
+ * is not actually in this window and a different window (which
+ * has that one hash excess) must be used. This is to support
+ * the object header and delta base parsing routines below.
+ */
+ off_t win_off = win->offset;
+ return win_off <= offset
+ && (offset + 20) <= (win_off + win->len);
+}
+
unsigned char* use_pack(struct packed_git *p,
struct pack_window **w_cursor,
unsigned long offset,
unsigned int *left)
{
- struct pack_window *win = p->windows;
+ struct pack_window *win = *w_cursor;
if (p->pack_fd == -1)
open_packed_git(p);
- if (!win) {
- pack_mapped += p->pack_size;
- while (packed_git_limit < pack_mapped && unuse_one_window())
- ; /* nothing */
- win = xcalloc(1, sizeof(*win));
- win->len = p->pack_size;
- win->base = mmap(NULL, p->pack_size, PROT_READ, MAP_PRIVATE, p->pack_fd, 0);
- if (win->base == MAP_FAILED)
- die("packfile %s cannot be mapped.", p->pack_name);
- p->windows = win;
+
+ /* Since packfiles end in a hash of their content and its
+ * pointless to ask for an offset into the middle of that
+ * hash, and the in_window function above wouldn't match
+ * don't allow an offset too close to the end of the file.
+ */
+ if (offset > (p->pack_size - 20))
+ die("offset beyond end of packfile (truncated pack?)");
+
+ if (!win || !in_window(win, offset)) {
+ if (win)
+ win->inuse_cnt--;
+ for (win = p->windows; win; win = win->next) {
+ if (in_window(win, offset))
+ break;
+ }
+ if (!win) {
+ if (!page_size)
+ page_size = getpagesize();
+ win = xcalloc(1, sizeof(*win));
+ win->offset = (offset / page_size) * page_size;
+ win->len = p->pack_size - win->offset;
+ if (win->len > packed_git_window_size)
+ win->len = packed_git_window_size;
+ pack_mapped += win->len;
+ while (packed_git_limit < pack_mapped && unuse_one_window())
+ ; /* nothing */
+ win->base = mmap(NULL, win->len,
+ PROT_READ, MAP_PRIVATE,
+ p->pack_fd, win->offset);
+ if (win->base == MAP_FAILED)
+ die("packfile %s cannot be mapped.", p->pack_name);
+ win->next = p->windows;
+ p->windows = win;
+ }
}
if (win != *w_cursor) {
win->last_used = pack_used_ctr++;
win->inuse_cnt++;
*w_cursor = win;
}
+ offset -= win->offset;
if (left)
*left = win->len - offset;
return win->base + offset;