From 650d30d8a120c8982309ccb9ef40432b4ea2eb74 Mon Sep 17 00:00:00 2001 From: Andreas Ericsson Date: Mon, 29 Jun 2009 11:55:51 +0200 Subject: [PATCH 01/25] mailinfo: Remove only one set of square brackets git-format-patch prepends patches with a [PATCH x/n] prefix, but mailinfo used to remove any number of square-bracket pairs and the content between them. This prevents one from using a commit subject like this: [ and ] must be allowed as input Removing the square bracket pair from this rather clumsily constructed subject line loses important information, so we must take care not to. This patch causes the subject stripping to stop after it has encountered one pair of square brackets. One possible downside of this patch is that the patch-handling programs will now fail at removing author-added square-brackets to be removed, such as [RFC][PATCH x/n] However, since format-patch only adds one set of square brackets, this behaviour is quite easily undesrstood and defended while the previous behaviour is not. Signed-off-by: Andreas Ericsson Signed-off-by: Junio C Hamano --- builtin-mailinfo.c | 7 +++++++ t/t5100/info0012 | 2 +- t/t5100/sample.mbox | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index 1eeeb4de6d..be42532d43 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -222,6 +222,8 @@ static void cleanup_subject(struct strbuf *subject) { char *pos; size_t remove; + int brackets_removed = 0; + while (subject->len) { switch (*subject->buf) { case 'r': case 'R': @@ -236,10 +238,15 @@ static void cleanup_subject(struct strbuf *subject) strbuf_remove(subject, 0, 1); continue; case '[': + /* remove only one set of square brackets */ + if (brackets_removed) + break; + if ((pos = strchr(subject->buf, ']'))) { remove = pos - subject->buf; if (remove <= (subject->len - remove) * 2) { strbuf_remove(subject, 0, remove + 1); + brackets_removed = 1; continue; } } else diff --git a/t/t5100/info0012 b/t/t5100/info0012 index ac1216ff75..9cd1415d25 100644 --- a/t/t5100/info0012 +++ b/t/t5100/info0012 @@ -1,5 +1,5 @@ Author: Dmitriy Blinov Email: bda@mnsspb.ru -Subject: Изменён список пакетов необходимых для сборки +Subject: [Navy-patches] Изменён список пакетов необходимых для сборки Date: Wed, 12 Nov 2008 17:54:41 +0300 diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox index c5ad206b40..ba4d0c9baf 100644 --- a/t/t5100/sample.mbox +++ b/t/t5100/sample.mbox @@ -514,7 +514,7 @@ MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit -Subject: [Navy-patches] [PATCH] +Subject: [PATCH] [Navy-patches] =?utf-8?b?0JjQt9C80LXQvdGR0L0g0YHQv9C40YHQvtC6INC/0LA=?= =?utf-8?b?0LrQtdGC0L7QsiDQvdC10L7QsdGF0L7QtNC40LzRi9GFINC00LvRjyA=?= =?utf-8?b?0YHQsdC+0YDQutC4?= From 1d8842d921cc2695f155f4a10904eeffad085c77 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 14 May 2009 13:22:36 -0700 Subject: [PATCH 02/25] Add 'fill_directory()' helper function for directory traversal Most of the users of "read_directory()" actually want a much simpler interface than the whole complex (but rather powerful) one. In fact 'git add' had already largely abstracted out the core interface issues into a private "fill_directory()" function that was largely applicable almost as-is to a number of callers. Yes, 'git add' wants to do some extra work of its own, specific to the add semantics, but we can easily split that out, and use the core as a generic function. This function does exactly that, and now that much simplified 'fill_directory()' function can be shared with a number of callers, while also ensuring that the rather more complex calling conventions of read_directory() are used by fewer call-sites. This also makes the 'common_prefix()' helper function private to dir.c, since all callers are now in that file. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-add.c | 45 ++++++++++++++------------------------------- builtin-clean.c | 12 +----------- builtin-ls-files.c | 7 +------ dir.c | 23 ++++++++++++++++++++++- dir.h | 3 +-- wt-status.c | 2 +- 6 files changed, 40 insertions(+), 52 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index 78989dad8c..581a2a1748 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -97,35 +97,6 @@ static void treat_gitlinks(const char **pathspec) } } -static void fill_directory(struct dir_struct *dir, const char **pathspec, - int ignored_too) -{ - const char *path, *base; - int baselen; - - /* Set up the default git porcelain excludes */ - memset(dir, 0, sizeof(*dir)); - if (!ignored_too) { - dir->flags |= DIR_COLLECT_IGNORED; - setup_standard_excludes(dir); - } - - /* - * Calculate common prefix for the pathspec, and - * use that to optimize the directory walk - */ - baselen = common_prefix(pathspec); - path = "."; - base = ""; - if (baselen) - path = base = xmemdupz(*pathspec, baselen); - - /* Read the directory and prune it */ - read_directory(dir, path, base, baselen, pathspec); - if (pathspec) - prune_directory(dir, pathspec, baselen); -} - static void refresh(int verbose, const char **pathspec) { char *seen; @@ -343,9 +314,21 @@ int cmd_add(int argc, const char **argv, const char *prefix) die("index file corrupt"); treat_gitlinks(pathspec); - if (add_new_files) + if (add_new_files) { + int baselen; + + /* Set up the default git porcelain excludes */ + memset(&dir, 0, sizeof(dir)); + if (!ignored_too) { + dir.flags |= DIR_COLLECT_IGNORED; + setup_standard_excludes(&dir); + } + /* This picks up the paths that are not tracked */ - fill_directory(&dir, pathspec, ignored_too); + baselen = fill_directory(&dir, pathspec); + if (pathspec) + prune_directory(&dir, pathspec, baselen); + } if (refresh_only) { refresh(verbose, pathspec); diff --git a/builtin-clean.c b/builtin-clean.c index 1c1b6d26e9..2d8c735d48 100644 --- a/builtin-clean.c +++ b/builtin-clean.c @@ -33,7 +33,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix) int ignored_only = 0, baselen = 0, config_set = 0, errors = 0; struct strbuf directory = STRBUF_INIT; struct dir_struct dir; - const char *path, *base; static const char **pathspec; struct strbuf buf = STRBUF_INIT; const char *qname; @@ -78,16 +77,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) pathspec = get_pathspec(prefix, argv); read_cache(); - /* - * Calculate common prefix for the pathspec, and - * use that to optimize the directory walk - */ - baselen = common_prefix(pathspec); - path = "."; - base = ""; - if (baselen) - path = base = xmemdupz(*pathspec, baselen); - read_directory(&dir, path, base, baselen, pathspec); + fill_directory(&dir, pathspec); if (pathspec) seen = xmalloc(argc > 0 ? argc : 1); diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 2312866605..f473220502 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -161,12 +161,7 @@ static void show_files(struct dir_struct *dir, const char *prefix) /* For cached/deleted files we don't need to even do the readdir */ if (show_others || show_killed) { - const char *path = ".", *base = ""; - int baselen = prefix_len; - - if (baselen) - path = base = prefix; - read_directory(dir, path, base, baselen, pathspec); + fill_directory(dir, pathspec); if (show_others) show_other_files(dir); if (show_killed) diff --git a/dir.c b/dir.c index 74b3bbf6fd..0c8553b27c 100644 --- a/dir.c +++ b/dir.c @@ -19,7 +19,7 @@ static int read_directory_recursive(struct dir_struct *dir, int check_only, const struct path_simplify *simplify); static int get_dtype(struct dirent *de, const char *path); -int common_prefix(const char **pathspec) +static int common_prefix(const char **pathspec) { const char *path, *slash, *next; int prefix; @@ -52,6 +52,27 @@ int common_prefix(const char **pathspec) return prefix; } +int fill_directory(struct dir_struct *dir, const char **pathspec) +{ + const char *path, *base; + int baselen; + + /* + * Calculate common prefix for the pathspec, and + * use that to optimize the directory walk + */ + baselen = common_prefix(pathspec); + path = ""; + base = ""; + + if (baselen) + path = base = xmemdupz(*pathspec, baselen); + + /* Read the directory and prune it */ + read_directory(dir, path, base, baselen, pathspec); + return baselen; +} + /* * Does 'match' match the given name? * A match is found if diff --git a/dir.h b/dir.h index 541286ad1d..f9d69dd15f 100644 --- a/dir.h +++ b/dir.h @@ -61,13 +61,12 @@ struct dir_struct { char basebuf[PATH_MAX]; }; -extern int common_prefix(const char **pathspec); - #define MATCHED_RECURSIVELY 1 #define MATCHED_FNMATCH 2 #define MATCHED_EXACTLY 3 extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen); +extern int fill_directory(struct dir_struct *dir, const char **pathspec); extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec); extern int excluded(struct dir_struct *, const char *, int *); diff --git a/wt-status.c b/wt-status.c index 0ca4b13c29..47735d8129 100644 --- a/wt-status.c +++ b/wt-status.c @@ -255,7 +255,7 @@ static void wt_status_print_untracked(struct wt_status *s) DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; setup_standard_excludes(&dir); - read_directory(&dir, ".", "", 0, NULL); + fill_directory(&dir, NULL); for(i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (!cache_name_is_other(ent->name, ent->len)) From dba2e2037f40685bffc87d3e7114a02c5bda1eff Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 8 Jul 2009 19:24:39 -0700 Subject: [PATCH 03/25] Simplify read_directory[_recursive]() arguments Stop the insanity with separate 'path' and 'base' arguments that must match. We don't need that crazy interface any more, since we cleaned up handling of 'path' in commit da4b3e8c28b1dc2b856d2555ac7bb47ab712598c. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- dir.c | 57 +++++++++++++++++++++++++------------------------- dir.h | 2 +- unpack-trees.c | 2 +- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/dir.c b/dir.c index 0c8553b27c..b0671f59c8 100644 --- a/dir.c +++ b/dir.c @@ -14,8 +14,7 @@ struct path_simplify { const char *path; }; -static int read_directory_recursive(struct dir_struct *dir, - const char *path, const char *base, int baselen, +static int read_directory_recursive(struct dir_struct *dir, const char *path, int len, int check_only, const struct path_simplify *simplify); static int get_dtype(struct dirent *de, const char *path); @@ -54,23 +53,22 @@ static int common_prefix(const char **pathspec) int fill_directory(struct dir_struct *dir, const char **pathspec) { - const char *path, *base; - int baselen; + const char *path; + int len; /* * Calculate common prefix for the pathspec, and * use that to optimize the directory walk */ - baselen = common_prefix(pathspec); + len = common_prefix(pathspec); path = ""; - base = ""; - if (baselen) - path = base = xmemdupz(*pathspec, baselen); + if (len) + path = xmemdupz(*pathspec, len); /* Read the directory and prune it */ - read_directory(dir, path, base, baselen, pathspec); - return baselen; + read_directory(dir, path, len, pathspec); + return len; } /* @@ -526,7 +524,7 @@ static enum directory_treatment treat_directory(struct dir_struct *dir, /* This is the "show_other_directories" case */ if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES)) return show_directory; - if (!read_directory_recursive(dir, dirname, dirname, len, 1, simplify)) + if (!read_directory_recursive(dir, dirname, len, 1, simplify)) return ignore_directory; return show_directory; } @@ -595,15 +593,15 @@ static int get_dtype(struct dirent *de, const char *path) * Also, we ignore the name ".git" (even if it is not a directory). * That likely will not change. */ -static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only, const struct path_simplify *simplify) +static int read_directory_recursive(struct dir_struct *dir, const char *base, int baselen, int check_only, const struct path_simplify *simplify) { - DIR *fdir = opendir(*path ? path : "."); + DIR *fdir = opendir(*base ? base : "."); int contents = 0; if (fdir) { struct dirent *de; - char fullname[PATH_MAX + 1]; - memcpy(fullname, base, baselen); + char path[PATH_MAX + 1]; + memcpy(path, base, baselen); while ((de = readdir(fdir)) != NULL) { int len, dtype; @@ -614,17 +612,18 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co continue; len = strlen(de->d_name); /* Ignore overly long pathnames! */ - if (len + baselen + 8 > sizeof(fullname)) + if (len + baselen + 8 > sizeof(path)) continue; - memcpy(fullname + baselen, de->d_name, len+1); - if (simplify_away(fullname, baselen + len, simplify)) + memcpy(path + baselen, de->d_name, len+1); + len = baselen + len; + if (simplify_away(path, len, simplify)) continue; dtype = DTYPE(de); - exclude = excluded(dir, fullname, &dtype); + exclude = excluded(dir, path, &dtype); if (exclude && (dir->flags & DIR_COLLECT_IGNORED) - && in_pathspec(fullname, baselen + len, simplify)) - dir_add_ignored(dir, fullname, baselen + len); + && in_pathspec(path, len, simplify)) + dir_add_ignored(dir, path,len); /* * Excluded? If we don't explicitly want to show @@ -634,7 +633,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co continue; if (dtype == DT_UNKNOWN) - dtype = get_dtype(de, fullname); + dtype = get_dtype(de, path); /* * Do we want to see just the ignored files? @@ -651,9 +650,9 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co default: continue; case DT_DIR: - memcpy(fullname + baselen + len, "/", 2); + memcpy(path + len, "/", 2); len++; - switch (treat_directory(dir, fullname, baselen + len, simplify)) { + switch (treat_directory(dir, path, len, simplify)) { case show_directory: if (exclude != !!(dir->flags & DIR_SHOW_IGNORED)) @@ -661,7 +660,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co break; case recurse_into_directory: contents += read_directory_recursive(dir, - fullname, fullname, baselen + len, 0, simplify); + path, len, 0, simplify); continue; case ignore_directory: continue; @@ -675,7 +674,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co if (check_only) goto exit_early; else - dir_add_name(dir, fullname, baselen + len); + dir_add_name(dir, path, len); } exit_early: closedir(fdir); @@ -738,15 +737,15 @@ static void free_simplify(struct path_simplify *simplify) free(simplify); } -int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec) +int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec) { struct path_simplify *simplify; - if (has_symlink_leading_path(path, strlen(path))) + if (has_symlink_leading_path(path, len)) return dir->nr; simplify = create_simplify(pathspec); - read_directory_recursive(dir, path, base, baselen, 0, simplify); + read_directory_recursive(dir, path, len, 0, simplify); free_simplify(simplify); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name); diff --git a/dir.h b/dir.h index f9d69dd15f..a6314464f9 100644 --- a/dir.h +++ b/dir.h @@ -67,7 +67,7 @@ struct dir_struct { extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen); extern int fill_directory(struct dir_struct *dir, const char **pathspec); -extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec); +extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec); extern int excluded(struct dir_struct *, const char *, int *); extern void add_excludes_from_file(struct dir_struct *, const char *fname); diff --git a/unpack-trees.c b/unpack-trees.c index 05d0bb1f85..42c7d7d563 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -551,7 +551,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action, memset(&d, 0, sizeof(d)); if (o->dir) d.exclude_per_dir = o->dir->exclude_per_dir; - i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL); + i = read_directory(&d, pathbuf, namelen+1, NULL); if (i) return o->gently ? -1 : error(ERRORMSG(o, not_uptodate_dir), ce->name); From caa6b7825aaddb8a97a5b793ca61df0c1ec9b76b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 8 Jul 2009 19:31:49 -0700 Subject: [PATCH 04/25] Avoid doing extra 'lstat()'s for d_type if we have an up-to-date cache entry On filesystems without d_type, we can look at the cache entry first. Doing an lstat() can be expensive. Reported by Dmitry Potapov for Cygwin. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- dir.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/dir.c b/dir.c index b0671f59c8..8a9e7d8131 100644 --- a/dir.c +++ b/dir.c @@ -16,7 +16,7 @@ struct path_simplify { static int read_directory_recursive(struct dir_struct *dir, const char *path, int len, int check_only, const struct path_simplify *simplify); -static int get_dtype(struct dirent *de, const char *path); +static int get_dtype(struct dirent *de, const char *path, int len); static int common_prefix(const char **pathspec) { @@ -326,7 +326,7 @@ static int excluded_1(const char *pathname, if (x->flags & EXC_FLAG_MUSTBEDIR) { if (*dtype == DT_UNKNOWN) - *dtype = get_dtype(NULL, pathname); + *dtype = get_dtype(NULL, pathname, pathlen); if (*dtype != DT_DIR) continue; } @@ -566,14 +566,18 @@ static int in_pathspec(const char *path, int len, const struct path_simplify *si return 0; } -static int get_dtype(struct dirent *de, const char *path) +static int get_dtype(struct dirent *de, const char *path, int len) { int dtype = de ? DTYPE(de) : DT_UNKNOWN; + struct cache_entry *ce; struct stat st; if (dtype != DT_UNKNOWN) return dtype; - if (lstat(path, &st)) + ce = cache_name_exists(path, len, 0); + if (ce && ce_uptodate(ce)) + st.st_mode = ce->ce_mode; + else if (lstat(path, &st)) return dtype; if (S_ISREG(st.st_mode)) return DT_REG; @@ -633,7 +637,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *base, in continue; if (dtype == DT_UNKNOWN) - dtype = get_dtype(de, path); + dtype = get_dtype(de, path, len); /* * Do we want to see just the ignored files? From 443e061a41bee30de34793648793ed70477ac575 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 9 Jul 2009 13:14:28 -0700 Subject: [PATCH 05/25] Avoid using 'lstat()' to figure out directories If we have an up-to-date index entry for a file in that directory, we can know that the directories leading up to that file must be directories. No need to do an lstat() on the directory. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- dir.c | 47 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/dir.c b/dir.c index 8a9e7d8131..e05b850acf 100644 --- a/dir.c +++ b/dir.c @@ -566,18 +566,55 @@ static int in_pathspec(const char *path, int len, const struct path_simplify *si return 0; } +static int get_index_dtype(const char *path, int len) +{ + int pos; + struct cache_entry *ce; + + ce = cache_name_exists(path, len, 0); + if (ce) { + if (!ce_uptodate(ce)) + return DT_UNKNOWN; + if (S_ISGITLINK(ce->ce_mode)) + return DT_DIR; + /* + * Nobody actually cares about the + * difference between DT_LNK and DT_REG + */ + return DT_REG; + } + + /* Try to look it up as a directory */ + pos = cache_name_pos(path, len); + if (pos >= 0) + return DT_UNKNOWN; + pos = -pos-1; + while (pos < active_nr) { + ce = active_cache[pos++]; + if (strncmp(ce->name, path, len)) + break; + if (ce->name[len] > '/') + break; + if (ce->name[len] < '/') + continue; + if (!ce_uptodate(ce)) + break; /* continue? */ + return DT_DIR; + } + return DT_UNKNOWN; +} + static int get_dtype(struct dirent *de, const char *path, int len) { int dtype = de ? DTYPE(de) : DT_UNKNOWN; - struct cache_entry *ce; struct stat st; if (dtype != DT_UNKNOWN) return dtype; - ce = cache_name_exists(path, len, 0); - if (ce && ce_uptodate(ce)) - st.st_mode = ce->ce_mode; - else if (lstat(path, &st)) + dtype = get_index_dtype(path, len); + if (dtype != DT_UNKNOWN) + return dtype; + if (lstat(path, &st)) return dtype; if (S_ISREG(st.st_mode)) return DT_REG; From 867f72bf434a05b9eadf851a81564be5173fbba5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 9 Jul 2009 13:23:59 -0700 Subject: [PATCH 06/25] Prepare symlink caching for thread-safety This doesn't actually change the external interfaces, so they are still thread-unsafe, but it makes the code internally pass a pointer to a local 'struct cache_def' around, so that the core code can be made thread-safe. The threaded index preloading will want to verify that the paths leading up to a pathname are all real directories. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- symlinks.c | 75 +++++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/symlinks.c b/symlinks.c index 8dcd63261f..08ad35330f 100644 --- a/symlinks.c +++ b/symlinks.c @@ -38,13 +38,13 @@ static struct cache_def { int flags; int track_flags; int prefix_len_stat_func; -} cache; +} default_cache; -static inline void reset_lstat_cache(void) +static inline void reset_lstat_cache(struct cache_def *cache) { - cache.path[0] = '\0'; - cache.len = 0; - cache.flags = 0; + cache->path[0] = '\0'; + cache->len = 0; + cache->flags = 0; /* * The track_flags and prefix_len_stat_func members is only * set by the safeguard rule inside lstat_cache() @@ -70,23 +70,23 @@ static inline void reset_lstat_cache(void) * of the prefix, where the cache should use the stat() function * instead of the lstat() function to test each path component. */ -static int lstat_cache(const char *name, int len, +static int lstat_cache(struct cache_def *cache, const char *name, int len, int track_flags, int prefix_len_stat_func) { int match_len, last_slash, last_slash_dir, previous_slash; int match_flags, ret_flags, save_flags, max_len, ret; struct stat st; - if (cache.track_flags != track_flags || - cache.prefix_len_stat_func != prefix_len_stat_func) { + if (cache->track_flags != track_flags || + cache->prefix_len_stat_func != prefix_len_stat_func) { /* * As a safeguard rule we clear the cache if the * values of track_flags and/or prefix_len_stat_func * does not match with the last supplied values. */ - reset_lstat_cache(); - cache.track_flags = track_flags; - cache.prefix_len_stat_func = prefix_len_stat_func; + reset_lstat_cache(cache); + cache->track_flags = track_flags; + cache->prefix_len_stat_func = prefix_len_stat_func; match_len = last_slash = 0; } else { /* @@ -94,10 +94,10 @@ static int lstat_cache(const char *name, int len, * the 2 "excluding" path types. */ match_len = last_slash = - longest_path_match(name, len, cache.path, cache.len, + longest_path_match(name, len, cache->path, cache->len, &previous_slash); - match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK); - if (match_flags && match_len == cache.len) + match_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK); + if (match_flags && match_len == cache->len) return match_flags; /* * If we now have match_len > 0, we would know that @@ -121,18 +121,18 @@ static int lstat_cache(const char *name, int len, max_len = len < PATH_MAX ? len : PATH_MAX; while (match_len < max_len) { do { - cache.path[match_len] = name[match_len]; + cache->path[match_len] = name[match_len]; match_len++; } while (match_len < max_len && name[match_len] != '/'); if (match_len >= max_len && !(track_flags & FL_FULLPATH)) break; last_slash = match_len; - cache.path[last_slash] = '\0'; + cache->path[last_slash] = '\0'; if (last_slash <= prefix_len_stat_func) - ret = stat(cache.path, &st); + ret = stat(cache->path, &st); else - ret = lstat(cache.path, &st); + ret = lstat(cache->path, &st); if (ret) { ret_flags = FL_LSTATERR; @@ -156,9 +156,9 @@ static int lstat_cache(const char *name, int len, */ save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK); if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) { - cache.path[last_slash] = '\0'; - cache.len = last_slash; - cache.flags = save_flags; + cache->path[last_slash] = '\0'; + cache->len = last_slash; + cache->flags = save_flags; } else if ((track_flags & FL_DIR) && last_slash_dir > 0 && last_slash_dir <= PATH_MAX) { /* @@ -172,11 +172,11 @@ static int lstat_cache(const char *name, int len, * can still cache the path components before the last * one (the found symlink or non-existing component). */ - cache.path[last_slash_dir] = '\0'; - cache.len = last_slash_dir; - cache.flags = FL_DIR; + cache->path[last_slash_dir] = '\0'; + cache->len = last_slash_dir; + cache->flags = FL_DIR; } else { - reset_lstat_cache(); + reset_lstat_cache(cache); } return ret_flags; } @@ -188,16 +188,17 @@ static int lstat_cache(const char *name, int len, void invalidate_lstat_cache(const char *name, int len) { int match_len, previous_slash; + struct cache_def *cache = &default_cache; /* FIXME */ - match_len = longest_path_match(name, len, cache.path, cache.len, + match_len = longest_path_match(name, len, cache->path, cache->len, &previous_slash); if (len == match_len) { - if ((cache.track_flags & FL_DIR) && previous_slash > 0) { - cache.path[previous_slash] = '\0'; - cache.len = previous_slash; - cache.flags = FL_DIR; + if ((cache->track_flags & FL_DIR) && previous_slash > 0) { + cache->path[previous_slash] = '\0'; + cache->len = previous_slash; + cache->flags = FL_DIR; } else { - reset_lstat_cache(); + reset_lstat_cache(cache); } } } @@ -207,7 +208,8 @@ void invalidate_lstat_cache(const char *name, int len) */ void clear_lstat_cache(void) { - reset_lstat_cache(); + struct cache_def *cache = &default_cache; /* FIXME */ + reset_lstat_cache(cache); } #define USE_ONLY_LSTAT 0 @@ -217,7 +219,8 @@ void clear_lstat_cache(void) */ int has_symlink_leading_path(const char *name, int len) { - return lstat_cache(name, len, + struct cache_def *cache = &default_cache; /* FIXME */ + return lstat_cache(cache, name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK; } @@ -228,7 +231,8 @@ int has_symlink_leading_path(const char *name, int len) */ int has_symlink_or_noent_leading_path(const char *name, int len) { - return lstat_cache(name, len, + struct cache_def *cache = &default_cache; /* FIXME */ + return lstat_cache(cache, name, len, FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) & (FL_SYMLINK|FL_NOENT); } @@ -242,7 +246,8 @@ int has_symlink_or_noent_leading_path(const char *name, int len) */ int has_dirs_only_path(const char *name, int len, int prefix_len) { - return lstat_cache(name, len, + struct cache_def *cache = &default_cache; /* FIXME */ + return lstat_cache(cache, name, len, FL_DIR|FL_FULLPATH, prefix_len) & FL_DIR; } From b9fd284657de3ec30922fb17c0baf243ae947fdd Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 9 Jul 2009 13:35:31 -0700 Subject: [PATCH 07/25] Export thread-safe version of 'has_symlink_leading_path()' The threaded index preloading will want it, so that it can avoid locking by simply using a per-thread symlink/directory cache. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- cache.h | 10 ++++++++++ symlinks.c | 21 ++++++++++----------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/cache.h b/cache.h index 871c9844e8..f1e5ede021 100644 --- a/cache.h +++ b/cache.h @@ -744,7 +744,17 @@ struct checkout { }; extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath); + +struct cache_def { + char path[PATH_MAX + 1]; + int len; + int flags; + int track_flags; + int prefix_len_stat_func; +}; + extern int has_symlink_leading_path(const char *name, int len); +extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int); extern int has_symlink_or_noent_leading_path(const char *name, int len); extern int has_dirs_only_path(const char *name, int len, int prefix_len); extern void invalidate_lstat_cache(const char *name, int len); diff --git a/symlinks.c b/symlinks.c index 08ad35330f..4bdded39c5 100644 --- a/symlinks.c +++ b/symlinks.c @@ -32,13 +32,7 @@ static int longest_path_match(const char *name_a, int len_a, return match_len; } -static struct cache_def { - char path[PATH_MAX + 1]; - int len; - int flags; - int track_flags; - int prefix_len_stat_func; -} default_cache; +static struct cache_def default_cache; static inline void reset_lstat_cache(struct cache_def *cache) { @@ -214,15 +208,20 @@ void clear_lstat_cache(void) #define USE_ONLY_LSTAT 0 +/* + * Return non-zero if path 'name' has a leading symlink component + */ +int threaded_has_symlink_leading_path(struct cache_def *cache, const char *name, int len) +{ + return lstat_cache(cache, name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK; +} + /* * Return non-zero if path 'name' has a leading symlink component */ int has_symlink_leading_path(const char *name, int len) { - struct cache_def *cache = &default_cache; /* FIXME */ - return lstat_cache(cache, name, len, - FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & - FL_SYMLINK; + return threaded_has_symlink_leading_path(&default_cache, name, len); } /* From f62ce3de9dd4803f50f65e17f5fc03c7bdb49c40 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 9 Jul 2009 13:37:02 -0700 Subject: [PATCH 08/25] Make index preloading check the whole path to the file This uses the new thread-safe 'threaded_has_symlink_leading_path()' function to efficiently verify that the whole path leading up to the filename is a proper path, and does not contain symlinks. This makes 'ce_uptodate()' a much stronger guarantee: it no longer just guarantees that the 'lstat()' of the path would match, it also means that we know that people haven't played games with moving directories around and covered it up with symlinks. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- preload-index.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/preload-index.c b/preload-index.c index 88edc5f8a9..14d5281183 100644 --- a/preload-index.c +++ b/preload-index.c @@ -34,7 +34,9 @@ static void *preload_thread(void *_data) struct thread_data *p = _data; struct index_state *index = p->index; struct cache_entry **cep = index->cache + p->offset; + struct cache_def cache; + memset(&cache, 0, sizeof(cache)); nr = p->nr; if (nr + p->offset > index->cache_nr) nr = index->cache_nr - p->offset; @@ -49,6 +51,8 @@ static void *preload_thread(void *_data) continue; if (!ce_path_match(ce, p->pathspec)) continue; + if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce))) + continue; if (lstat(ce->name, &st)) continue; if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY)) From 6edd14968bf969a651d057ea7fb7757393d282bf Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 10 Jul 2009 20:17:33 -0700 Subject: [PATCH 09/25] Makefile: keep "git" when bindir is execdir For some reason there still are people who use the old style layout to put everything in $(bindir). The previous commit breaks the install for them, because it tries to unconditionally remove git from execdir and cp/ln from bindir --- oops. Signed-off-by: Junio C Hamano --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 311ce7d745..4be508e877 100644 --- a/Makefile +++ b/Makefile @@ -1641,10 +1641,11 @@ ifneq (,$X) endif bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \ execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \ - { $(RM) "$$execdir/git$X" && \ + { test "$$bindir/" = "$$execdir/" || \ + { $(RM) "$$execdir/git$X" && \ test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \ ln "$$bindir/git$X" "$$execdir/git$X" 2>/dev/null || \ - cp "$$bindir/git$X" "$$execdir/git$X"; } && \ + cp "$$bindir/git$X" "$$execdir/git$X"; } ; } && \ { for p in $(BUILT_INS); do \ $(RM) "$$execdir/$$p" && \ ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \ From 0039ba7e5e630502be9ac601845b214abce93750 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Fri, 10 Jul 2009 12:10:43 -0500 Subject: [PATCH 10/25] unpack-trees.c: work around run-time array initialization flaw on IRIX 6.5 The c99 MIPSpro Compiler version 7.4.4m on IRIX 6.5 does not properly initialize run-time initialized arrays. An array which is initialized with fewer elements than the length of the array should have the unitialized elements initialized to zero. This compiler only initializes the remaining elements when the last element is a static parameter. So work around it by adding a "NULL" initialization parameter. Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- unpack-trees.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unpack-trees.c b/unpack-trees.c index 42c7d7d563..f9d12aafba 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -128,7 +128,7 @@ static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_o static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_options *o) { - struct cache_entry *src[5] = { ce, }; + struct cache_entry *src[5] = { ce, NULL, }; o->pos++; if (ce_stage(ce)) { From 9398b85994ee7d602e29e0b82de01b9605ee535f Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Fri, 10 Jul 2009 12:10:44 -0500 Subject: [PATCH 11/25] git-compat-util.h: adjust for SGI IRIX 6.5 Don't define _XOPEN_SOURCE Do define _SGI_SOURCE Defining _XOPEN_SOURCE prevents many of the common functions and macros from being defined. _Not_ setting _XOPEN_SOURCE, and instead setting _SGI_SOURCE, provides all of the XPG4, XPG5, BSD, POSIX functions and declarations, _BUT_ provides a horribly broken snprintf(). SGI does have a working snprintf(), but it is only provided when _NO_XOPEN5 evaluates to zero, and this only happens if _XOPEN_SOURCE is defined which, as mentioned above, prevents many other common functions and defines. The broken snprintf will be worked around with SNPRINTF_RETURNS_BOGUS in the Makefile in a later patch. Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- git-compat-util.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-compat-util.h b/git-compat-util.h index 9609eaa77f..913f41a42c 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -52,7 +52,7 @@ # else # define _XOPEN_SOURCE 500 # endif -#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX) +#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX) && !defined(sgi) #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */ #ifndef __sun__ #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */ @@ -62,6 +62,7 @@ #define _GNU_SOURCE 1 #define _BSD_SOURCE 1 #define _NETBSD_SOURCE 1 +#define _SGI_SOURCE 1 #include #include From ecc395c112b45b00df6c8b1e8d665123c1a28bca Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Fri, 10 Jul 2009 12:10:45 -0500 Subject: [PATCH 12/25] Makefile: add NEEDS_LIBGEN to optionally add -lgen to compile arguments Commit 003b33a8 recently added a call to basename(). On IRIX 6.5, this function resides in libgen and -lgen is required for the linker. Update configure.ac too. Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- Makefile | 5 +++++ configure.ac | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Makefile b/Makefile index 4be508e877..6d1a38fde1 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,8 @@ all:: # # Define NO_LIBGEN_H if you don't have libgen.h. # +# Define NEEDS_LIBGEN if your libgen needs -lgen when linking +# # Define NO_SYS_SELECT_H if you don't have sys/select.h. # # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link. @@ -1019,6 +1021,9 @@ ifdef NEEDS_LIBICONV endif EXTLIBS += $(ICONV_LINK) -liconv endif +ifdef NEEDS_LIBGEN + EXTLIBS += -lgen +endif ifdef NEEDS_SOCKET EXTLIBS += -lsocket endif diff --git a/configure.ac b/configure.ac index 1885674e39..74d0af52a5 100644 --- a/configure.ac +++ b/configure.ac @@ -485,6 +485,12 @@ AC_CHECK_LIB([resolv], [hstrerror], AC_SUBST(NEEDS_RESOLV) test -n "$NEEDS_RESOLV" && LIBS="$LIBS -lresolv" +AC_CHECK_LIB([gen], [basename], +[NEEDS_LIBGEN=], +[NEEDS_LIBGEN=YesPlease]) +AC_SUBST(NEEDS_LIBGEN) +test -n "$NEEDS_LIBGEN" && LIBS="$LIBS -lgen" + ## Checks for header files. AC_MSG_NOTICE([CHECKS for header files]) # From 1fdffc1bd8865fc07107d021d2fce7cd707b9b84 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Fri, 10 Jul 2009 12:10:46 -0500 Subject: [PATCH 13/25] Makefile: add section for SGI IRIX 6.5 Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- Makefile | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Makefile b/Makefile index 6d1a38fde1..ec24632259 100644 --- a/Makefile +++ b/Makefile @@ -830,6 +830,19 @@ ifeq ($(uname_S),GNU) NO_STRLCPY=YesPlease NO_MKSTEMPS = YesPlease endif +ifeq ($(uname_S),IRIX) + NO_SETENV = YesPlease + NO_UNSETENV = YesPlease + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease + NO_MKSTEMPS = YesPlease + NO_MKDTEMP = YesPlease + NO_MMAP = YesPlease + NO_EXTERNAL_GREP = UnfortunatelyYes + SNPRINTF_RETURNS_BOGUS = YesPlease + SHELL_PATH = /usr/gnu/bin/bash + NEEDS_LIBGEN = YesPlease +endif ifeq ($(uname_S),IRIX64) NO_IPV6=YesPlease NO_SETENV=YesPlease From 7c74ff50626faca2fd302d83610494dd9106896d Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Fri, 10 Jul 2009 13:31:19 -0500 Subject: [PATCH 14/25] Makefile: update IRIX64 section Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index ec24632259..bde27ed478 100644 --- a/Makefile +++ b/Makefile @@ -844,17 +844,17 @@ ifeq ($(uname_S),IRIX) NEEDS_LIBGEN = YesPlease endif ifeq ($(uname_S),IRIX64) - NO_IPV6=YesPlease NO_SETENV=YesPlease + NO_UNSETENV = YesPlease NO_STRCASESTR=YesPlease NO_MEMMEM = YesPlease NO_MKSTEMPS = YesPlease - NO_STRLCPY = YesPlease - NO_SOCKADDR_STORAGE=YesPlease + NO_MKDTEMP = YesPlease + NO_MMAP = YesPlease + NO_EXTERNAL_GREP = UnfortunatelyYes + SNPRINTF_RETURNS_BOGUS = YesPlease SHELL_PATH=/usr/gnu/bin/bash - BASIC_CFLAGS += -DPATH_MAX=1024 - # for now, build 32-bit version - BASIC_LDFLAGS += -L/usr/lib32 + NEEDS_LIBGEN = YesPlease endif ifeq ($(uname_S),HP-UX) NO_IPV6=YesPlease From d9eb0205a217984f3e70bf18ae66c02a22d3d475 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Fri, 10 Jul 2009 01:52:30 +0200 Subject: [PATCH 15/25] quickfetch(): Prevent overflow of the rev-list command line quickfetch() calls rev-list to check whether the objects we are about to fetch are already present in the repo (if so, we can skip the object fetch). However, when there are many (~1000) refs to be fetched, the rev-list command line grows larger than the maximum command line size on some systems (32K in Windows). This causes rev-list to fail, making quickfetch() return non-zero, which unnecessarily triggers the transport machinery. This somehow causes fetch to fail with an exit code. By using the --stdin option to rev-list (and feeding the object list to its standard input), we prevent the overflow of the rev-list command line, which causes quickfetch(), and subsequently the overall fetch, to succeed. However, using rev-list --stdin is not entirely straightforward: rev-list terminates immediately when encountering an unknown object, which can trigger SIGPIPE if we are still writing object's to its standard input. We therefore temporarily ignore SIGPIPE so that the fetch process is not terminated. The patch also contains a testcase to verify the fix (note that before the patch, the testcase would only fail on msysGit). Signed-off-by: Johan Herland Improved-by: Johannes Sixt Improved-by: Alex Riesen Tested-by: Peter Krefting Signed-off-by: Junio C Hamano --- builtin-fetch.c | 67 ++++++++++++++++++++++++++----------------- t/t5502-quickfetch.sh | 20 +++++++++++++ 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index cd5eb9aff5..817dd6bff0 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -400,14 +400,14 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, /* * We would want to bypass the object transfer altogether if - * everything we are going to fetch already exists and connected + * everything we are going to fetch already exists and is connected * locally. * - * The refs we are going to fetch are in to_fetch (nr_heads in - * total). If running + * The refs we are going to fetch are in ref_map. If running * - * $ git rev-list --objects to_fetch[0] to_fetch[1] ... --not --all + * $ git rev-list --objects --stdin --not --all * + * (feeding all the refs in ref_map on its standard input) * does not error out, that means everything reachable from the * refs we are going to fetch exists and is connected to some of * our existing refs. @@ -416,8 +416,9 @@ static int quickfetch(struct ref *ref_map) { struct child_process revlist; struct ref *ref; - char **argv; - int i, err; + int err; + const char *argv[] = {"rev-list", + "--quiet", "--objects", "--stdin", "--not", "--all", NULL}; /* * If we are deepening a shallow clone we already have these @@ -429,34 +430,46 @@ static int quickfetch(struct ref *ref_map) if (depth) return -1; - for (i = 0, ref = ref_map; ref; ref = ref->next) - i++; - if (!i) + if (!ref_map) return 0; - argv = xmalloc(sizeof(*argv) * (i + 6)); - i = 0; - argv[i++] = xstrdup("rev-list"); - argv[i++] = xstrdup("--quiet"); - argv[i++] = xstrdup("--objects"); - for (ref = ref_map; ref; ref = ref->next) - argv[i++] = xstrdup(sha1_to_hex(ref->old_sha1)); - argv[i++] = xstrdup("--not"); - argv[i++] = xstrdup("--all"); - argv[i++] = NULL; - memset(&revlist, 0, sizeof(revlist)); - revlist.argv = (const char**)argv; + revlist.argv = argv; revlist.git_cmd = 1; - revlist.no_stdin = 1; revlist.no_stdout = 1; revlist.no_stderr = 1; - err = run_command(&revlist); + revlist.in = -1; - for (i = 0; argv[i]; i++) - free(argv[i]); - free(argv); - return err; + err = start_command(&revlist); + if (err) { + error("could not run rev-list"); + return err; + } + + /* + * If rev-list --stdin encounters an unknown commit, it terminates, + * which will cause SIGPIPE in the write loop below. + */ + sigchain_push(SIGPIPE, SIG_IGN); + + for (ref = ref_map; ref; ref = ref->next) { + if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 || + write_in_full(revlist.in, "\n", 1) < 0) { + if (errno != EPIPE && errno != EINVAL) + error("failed write to rev-list: %s", strerror(errno)); + err = -1; + break; + } + } + + if (close(revlist.in)) { + error("failed to close rev-list's stdin: %s", strerror(errno)); + err = -1; + } + + sigchain_pop(SIGPIPE); + + return finish_command(&revlist) || err; } static int fetch_refs(struct transport *transport, struct ref *ref_map) diff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh index 16eadd6b68..1037a723fe 100755 --- a/t/t5502-quickfetch.sh +++ b/t/t5502-quickfetch.sh @@ -119,4 +119,24 @@ test_expect_success 'quickfetch should not copy from alternate' ' ' +test_expect_success 'quickfetch should handle ~1000 refs (on Windows)' ' + + git gc && + head=$(git rev-parse HEAD) && + branchprefix="$head refs/heads/branch" && + for i in 0 1 2 3 4 5 6 7 8 9; do + for j in 0 1 2 3 4 5 6 7 8 9; do + for k in 0 1 2 3 4 5 6 7 8 9; do + echo "$branchprefix$i$j$k" >> .git/packed-refs + done + done + done && + ( + cd cloned && + git fetch && + git fetch + ) + +' + test_done From 3c49a03524b686c7b575e0a667736217e5445447 Mon Sep 17 00:00:00 2001 From: Mattias Nissler Date: Tue, 7 Jul 2009 01:39:52 +0200 Subject: [PATCH 16/25] git-svn: Always duplicate paths returned from get_log This makes get_log more safe to use because callers cannot run into path clobbering any more. The additional overhead will not affect performance since the critical calls from the fetch loop need the path duplication anyway and the rest of the call sites is not performance critical. Signed-off-by: Mattias Nissler Acked-by: Eric Wong --- git-svn.perl | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index d1af1a3d2f..b4e8d44351 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -2538,9 +2538,8 @@ sub find_parent_branch { unless (defined $paths) { my $err_handler = $SVN::Error::handler; $SVN::Error::handler = \&Git::SVN::Ra::skip_unknown_revs; - $self->ra->get_log([$self->{path}], $rev, $rev, 0, 1, 1, sub { - $paths = - Git::SVN::Ra::dup_changed_paths($_[0]) }); + $self->ra->get_log([$self->{path}], $rev, $rev, 0, 1, 1, + sub { $paths = $_[0] }); $SVN::Error::handler = $err_handler; } return undef unless defined $paths; @@ -4431,6 +4430,26 @@ sub get_log { my ($self, @args) = @_; my $pool = SVN::Pool->new; + # svn_log_changed_path_t objects passed to get_log are likely to be + # overwritten even if only the refs are copied to an external variable, + # so we should dup the structures in their entirety. Using an + # externally passed pool (instead of our temporary and quickly cleared + # pool in Git::SVN::Ra) does not help matters at all... + my $receiver = pop @args; + push(@args, sub { + my ($paths) = $_[0]; + return &$receiver(@_) unless $paths; + $_[0] = (); + foreach my $p (keys %$paths) { + my $i = $paths->{$p}; + my %s = map { $_ => $i->$_ } + qw/copyfrom_path copyfrom_rev action/; + $_[0]{$p} = \%s; + } + &$receiver(@_); + }); + + # the limit parameter was not supported in SVN 1.1.x, so we # drop it. Therefore, the receiver callback passed to it # is made aware of this limitation by being wrapped if @@ -4600,7 +4619,7 @@ sub gs_fetch_loop_common { }; sub _cb { my ($paths, $r, $author, $date, $log) = @_; - [ dup_changed_paths($paths), + [ $paths, { author => $author, date => $date, log => $log } ]; } $self->get_log([$longest_path], $min, $max, 0, 1, 1, @@ -4823,24 +4842,6 @@ sub skip_unknown_revs { die "Error from SVN, ($errno): ", $err->expanded_message,"\n"; } -# svn_log_changed_path_t objects passed to get_log are likely to be -# overwritten even if only the refs are copied to an external variable, -# so we should dup the structures in their entirety. Using an externally -# passed pool (instead of our temporary and quickly cleared pool in -# Git::SVN::Ra) does not help matters at all... -sub dup_changed_paths { - my ($paths) = @_; - return undef unless $paths; - my %ret; - foreach my $p (keys %$paths) { - my $i = $paths->{$p}; - my %s = map { $_ => $i->$_ } - qw/copyfrom_path copyfrom_rev action/; - $ret{$p} = \%s; - } - \%ret; -} - package Git::SVN::Log; use strict; use warnings; From 0b2af457a49e3b00d47d556d5301934d27909db8 Mon Sep 17 00:00:00 2001 From: Mattias Nissler Date: Tue, 7 Jul 2009 01:40:02 +0200 Subject: [PATCH 17/25] git-svn: Fix branch detection when repository root is inaccessible For the case of multiple projects sharing a single SVN repository, it is common practice to create the standard SVN directory layout within a subdirectory for each project. In such setups, access control is often used to limit what projects a given user may access. git-svn failed to detect branches (e.g. when passing --stdlayout to clone) because it relied on having access to the root directory in the repository. This patch solves this problem by making git-svn use paths relative to the given repository URL instead of the repository root. Signed-off-by: Mattias Nissler Acked-by: Eric Wong --- git-svn.perl | 43 +++++++++++----------------- t/t9138-git-svn-multiple-branches.sh | 8 +++--- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index b4e8d44351..ec847580d6 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -876,10 +876,6 @@ sub cmd_multi_init { usage(1); } - # there are currently some bugs that prevent multi-init/multi-fetch - # setups from working well without this. - $Git::SVN::_minimize_url = 1; - $_prefix = '' unless defined $_prefix; if (defined $url) { $url = canonicalize_url($url); @@ -1180,7 +1176,7 @@ sub complete_url_ls_init { "wanted to set to: $gs->{url}\n"; } command_oneline('config', $k, $gs->{url}) unless $orig_url; - my $remote_path = "$ra->{svn_path}/$repo_path"; + my $remote_path = "$gs->{path}/$repo_path"; $remote_path =~ s#/+#/#g; $remote_path =~ s#^/##g; $remote_path .= "/*" if $remote_path !~ /\*/; @@ -2177,16 +2173,6 @@ sub ra { $ra; } -sub rel_path { - my ($self) = @_; - my $repos_root = $self->ra->{repos_root}; - return $self->{path} if ($self->{url} eq $repos_root); - my $url = $self->{url} . - (length $self->{path} ? "/$self->{path}" : $self->{path}); - $url =~ s!^\Q$repos_root\E(?:/+|$)!!g; - $url; -} - # prop_walk(PATH, REV, SUB) # ------------------------- # Recursively traverse PATH at revision REV and invoke SUB for each @@ -2512,10 +2498,7 @@ sub match_paths { if (my $path = $paths->{"/$self->{path}"}) { return ($path->{action} eq 'D') ? 0 : 1; } - my $repos_root = $self->ra->{repos_root}; - my $extended_path = $self->{url} . '/' . $self->{path}; - $extended_path =~ s#^\Q$repos_root\E(/|$)##; - $self->{path_regex} ||= qr/^\/\Q$extended_path\E\//; + $self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//; if (grep /$self->{path_regex}/, keys %$paths) { return 1; } @@ -2545,7 +2528,7 @@ sub find_parent_branch { return undef unless defined $paths; # look for a parent from another branch: - my @b_path_components = split m#/#, $self->rel_path; + my @b_path_components = split m#/#, $self->{path}; my @a_path_components; my $i; while (@b_path_components) { @@ -2563,11 +2546,11 @@ sub find_parent_branch { my $r = $i->{copyfrom_rev}; my $repos_root = $self->ra->{repos_root}; my $url = $self->ra->{url}; - my $new_url = $repos_root . $branch_from; + my $new_url = $url . $branch_from; print STDERR "Found possible branch point: ", "$new_url => ", $self->full_url, ", $r\n"; $branch_from =~ s#^/##; - my $gs = $self->other_gs($new_url, $url, $repos_root, + my $gs = $self->other_gs($new_url, $url, $branch_from, $r, $self->{ref_id}); my ($r0, $parent) = $gs->find_rev_before($r, 1); { @@ -2752,9 +2735,9 @@ sub parse_svn_date { } sub other_gs { - my ($self, $new_url, $url, $repos_root, + my ($self, $new_url, $url, $branch_from, $r, $old_ref_id) = @_; - my $gs = Git::SVN->find_by_url($new_url, $repos_root, $branch_from); + my $gs = Git::SVN->find_by_url($new_url, $url, $branch_from); unless ($gs) { my $ref_id = $old_ref_id; $ref_id =~ s/\@\d+$//; @@ -4436,14 +4419,22 @@ sub get_log { # externally passed pool (instead of our temporary and quickly cleared # pool in Git::SVN::Ra) does not help matters at all... my $receiver = pop @args; + my $prefix = "/".$self->{svn_path}; + $prefix =~ s#/+($)##; + my $prefix_regex = qr#^\Q$prefix\E#; push(@args, sub { my ($paths) = $_[0]; return &$receiver(@_) unless $paths; $_[0] = (); foreach my $p (keys %$paths) { my $i = $paths->{$p}; - my %s = map { $_ => $i->$_ } - qw/copyfrom_path copyfrom_rev action/; + # Make path relative to our url, not repos_root + $p =~ s/$prefix_regex//; + my %s = map { $_ => $i->$_; } + qw/copyfrom_path copyfrom_rev action/; + if ($s{'copyfrom_path'}) { + $s{'copyfrom_path'} =~ s/$prefix_regex//; + } $_[0]{$p} = \%s; } &$receiver(@_); diff --git a/t/t9138-git-svn-multiple-branches.sh b/t/t9138-git-svn-multiple-branches.sh index cb9a6d229d..3cd06718eb 100755 --- a/t/t9138-git-svn-multiple-branches.sh +++ b/t/t9138-git-svn-multiple-branches.sh @@ -99,22 +99,22 @@ test_expect_success 'Multiple branch or tag paths require -d' ' test_expect_success 'create new branches and tags' ' ( cd git_project && - git svn branch -m "New branch 1" -d project/b_one New1 ) && + git svn branch -m "New branch 1" -d b_one New1 ) && ( cd svn_project && svn_cmd up && test -e b_one/New1/a.file ) && ( cd git_project && - git svn branch -m "New branch 2" -d project/b_two New2 ) && + git svn branch -m "New branch 2" -d b_two New2 ) && ( cd svn_project && svn_cmd up && test -e b_two/New2/a.file ) && ( cd git_project && - git svn branch -t -m "New tag 1" -d project/tags_A Tag1 ) && + git svn branch -t -m "New tag 1" -d tags_A Tag1 ) && ( cd svn_project && svn_cmd up && test -e tags_A/Tag1/a.file ) && ( cd git_project && - git svn tag -m "New tag 2" -d project/tags_B Tag2 ) && + git svn tag -m "New tag 2" -d tags_B Tag2 ) && ( cd svn_project && svn_cmd up && test -e tags_B/Tag2/a.file ) ' From b3e95936274a7f5d516e7c37b39e1a56b2d09709 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 11 Jul 2009 14:13:12 -0700 Subject: [PATCH 18/25] git svn: allow uppercase UUIDs from SVN SVN allows uppercase A-F characters in repositories. Although `svnadmin' does not create UUIDs with uppercase by default, it is possible to change the UUID of a SVN repository and SVN itself will make no attempt to normalize them. Thanks to Esben Skovenborg for discovering this issue. Signed-off-by: Eric Wong --- git-svn.perl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index ec847580d6..cfade63459 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1359,11 +1359,11 @@ sub read_repo_config { sub extract_metadata { my $id = shift or return (undef, undef, undef); my ($url, $rev, $uuid) = ($id =~ /^\s*git-svn-id:\s+(.*)\@(\d+) - \s([a-f\d\-]+)$/x); + \s([a-f\d\-]+)$/ix); if (!defined $rev || !$uuid || !$url) { # some of the original repositories I made had # identifiers like this: - ($rev, $uuid) = ($id =~/^\s*git-svn-id:\s(\d+)\@([a-f\d\-]+)/); + ($rev, $uuid) = ($id =~/^\s*git-svn-id:\s(\d+)\@([a-f\d\-]+)/i); } return ($url, $rev, $uuid); } @@ -2010,7 +2010,7 @@ sub _set_svm_vars { chomp($src, $uuid); - $uuid =~ m{^[0-9a-f\-]{30,}$} + $uuid =~ m{^[0-9a-f\-]{30,}$}i or die "doesn't look right - svm:uuid is '$uuid'\n"; # the '!' is used to mark the repos_root!/relative/path @@ -2096,7 +2096,7 @@ sub svnsync { die "doesn't look right - svn:sync-from-url is '$url'\n"; my $uuid = tmp_config('--get', "$section.svnsync-uuid"); - ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}) or + ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}i) or die "doesn't look right - svn:sync-from-uuid is '$uuid'\n"; $svnsync = { url => $url, uuid => $uuid } @@ -2114,7 +2114,7 @@ sub svnsync { die "doesn't look right - svn:sync-from-url is '$url'\n"; my $uuid = $rp->{'svn:sync-from-uuid'} or die $err . "uuid\n"; - ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}) or + ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}i) or die "doesn't look right - svn:sync-from-uuid is '$uuid'\n"; my $section = "svn-remote.$self->{repo_id}"; @@ -2130,7 +2130,7 @@ sub ra_uuid { unless ($self->{ra_uuid}) { my $key = "svn-remote.$self->{repo_id}.uuid"; my $uuid = eval { tmp_config('--get', $key) }; - if (!$@ && $uuid && $uuid =~ /^([a-f\d\-]{30,})$/) { + if (!$@ && $uuid && $uuid =~ /^([a-f\d\-]{30,})$/i) { $self->{ra_uuid} = $uuid; } else { die "ra_uuid called without URL\n" unless $self->{url}; @@ -2848,7 +2848,7 @@ sub make_log_entry { die "Can't have both 'useSvmProps' and 'rewriteRoot' ", "options set!\n"; } - my ($uuid, $r) = $headrev =~ m{^([a-f\d\-]{30,}):(\d+)$}; + my ($uuid, $r) = $headrev =~ m{^([a-f\d\-]{30,}):(\d+)$}i; # we don't want "SVM: initializing mirror for junk" ... return undef if $r == 0; my $svm = $self->svm; From 69fb8283937a18a031aeef12ea2a530c8ccf3e83 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Sun, 12 Jul 2009 14:31:28 +0200 Subject: [PATCH 19/25] gitweb: update Git homepage URL git-scm.com is now the "official" Git project page, having taken over from git.or.cz, so update the default link accordingly. This saves a redirect when people hit git.or.cz. Signed-off-by: Wincent Colaiuta Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 6a1b5b5b49..7fbd5ff89e 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -94,7 +94,7 @@ our $favicon = "++GITWEB_FAVICON++"; # URI and label (title) of GIT logo link #our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/"; #our $logo_label = "git documentation"; -our $logo_url = "http://git.or.cz/"; +our $logo_url = "http://git-scm.com/"; our $logo_label = "git homepage"; # source of projects list From 2657420d9ecee5c68f300cdabdf2bec7e76a2d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 13 Jul 2009 17:11:44 +0200 Subject: [PATCH 20/25] Document 'git (rev-list|log) --merges' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- Documentation/git-rev-list.txt | 1 + Documentation/rev-list-options.txt | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 1c9cc28895..a765cfa4d2 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -14,6 +14,7 @@ SYNOPSIS [ \--max-age=timestamp ] [ \--min-age=timestamp ] [ \--sparse ] + [ \--merges ] [ \--no-merges ] [ \--first-parent ] [ \--remove-empty ] diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 11eec941df..bf66116d61 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -201,6 +201,10 @@ endif::git-rev-list[] Stop when a given path disappears from the tree. +--merges:: + + Print only merge commits. + --no-merges:: Do not print commits with more than one parent. From 4fe1a61973c82c459ac0a25cb5342d00d347dfd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 13 Jul 2009 17:11:45 +0200 Subject: [PATCH 21/25] bash: add '--merges' to common 'git log' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... so it's available for git log, shortlog and gitk. Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9c488646d0..887731e830 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1114,7 +1114,7 @@ _git_ls_tree () __git_log_common_options=" --not --all --branches --tags --remotes - --first-parent --no-merges + --first-parent --merges --no-merges --max-count= --max-age= --since= --after= --min-age= --until= --before= From f222abdeec7838891e79abd152c6cb67e532b68d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 13 Jul 2009 14:41:12 -0700 Subject: [PATCH 22/25] Make 'git show' more useful For some reason, I ended up doing git show HEAD~5.. as an odd way of asking for a log. I realize I should just have used "git log", but at the same time it does make perfect conceptual sense. After all, you _could_ have done git show HEAD HEAD~1 HEAD~2 HEAD~3 HEAD~4 and saying "git show HEAD~5.." is pretty natural. It's not like "git show" only ever showed a single commit (or other object) before either! So conceptually, giving a commit range is a very sensible operation, even though you'd traditionally have used "git log" for that. However, doing that currently results in an error fatal: object ranges do not make sense when not walking revisions which admittedly _also_ makes perfect sense - from an internal git implementation standpoint in 'revision.c'. However, I think that asking to show a range makes sense to a user, while saying "object ranges no not make sense when not walking revisions" only makes sense to a git developer. So on the whole, of the two different "makes perfect sense" behaviors, I think I originally picked the wrong one. And quite frankly, I don't really see anybody actually _depending_ on that error case. So why not change it? So rather than error out, just turn that non-walking error case into a "silently turn on walking" instead. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- revision.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/revision.c b/revision.c index a31434bdc8..9f5dac5f1d 100644 --- a/revision.c +++ b/revision.c @@ -133,7 +133,7 @@ void mark_parents_uninteresting(struct commit *commit) static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode) { if (revs->no_walk && (obj->flags & UNINTERESTING)) - die("object ranges do not make sense when not walking revisions"); + revs->no_walk = 0; if (revs->reflog_info && obj->type == OBJ_COMMIT && add_reflog_for_walk(revs->reflog_info, (struct commit *)obj, name)) From a38837341c995773f2adc29ff5971196187b07fb Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 14 Jul 2009 11:25:17 -0700 Subject: [PATCH 23/25] Improve on the 'invalid object' error message at commit time Not that anybody should ever get it, but somebody did (probably because of a flaky filesystem, but whatever). And each time I see an error message that I haven't seen before, I decide that next time it will look better. So this makes us write more relevant information about exactly which file ended up having issues with a missing object. Which will tell whether it was a tree object, for example, or just a regular file in the index (and which one). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- cache-tree.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cache-tree.c b/cache-tree.c index 16a65dfac1..d91743775d 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -329,7 +329,8 @@ static int update_one(struct cache_tree *it, entlen = pathlen - baselen; } if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) - return error("invalid object %s", sha1_to_hex(sha1)); + return error("invalid object %06o %s for '%.*s'", + mode, sha1_to_hex(sha1), entlen+baselen, path); if (ce->ce_flags & CE_REMOVE) continue; /* entry being removed */ From 05c1da2f5e7a5c0d2bbd1154bbb6c5cc9cb2b17f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 14 Jul 2009 14:19:10 -0700 Subject: [PATCH 24/25] Fix extraneous lstat's in 'git checkout -f' In our 'oneway_merge()' we always do an 'lstat()' to see if we might need to mark the entry for updating. But we really shouldn't need to do that when the cache entry is already marked as being ce_uptodate(), and this makes us do unnecessary lstat() calls if we have index preloading enabled. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- unpack-trees.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unpack-trees.c b/unpack-trees.c index f9d12aafba..48d862d3b4 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1004,7 +1004,7 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o) if (old && same(old, a)) { int update = 0; - if (o->reset) { + if (o->reset && !ce_uptodate(old)) { struct stat st; if (lstat(old->name, &st) || ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID)) From 4525e8e41a8c0c4246a95097744604ea72198dad Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 15 Jul 2009 15:10:06 -0700 Subject: [PATCH 25/25] Revert "mailinfo: Remove only one set of square brackets" This reverts commit 650d30d8a120c8982309ccb9ef40432b4ea2eb74. Some mailing lists are configured add prefix "[listname] " to all their messages, and also people hand-edit subject lines, be it an output from format-patch or a patch generated by some other means. We cannot stop people from mucking with the subject line, and with the change, there always will be need for hand editing the subject when that happens. People have depended on the leading [bracketed string] removal. --- builtin-mailinfo.c | 7 ------- t/t5100/info0012 | 2 +- t/t5100/sample.mbox | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index fb5ad70f3f..92637ac0ba 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -221,8 +221,6 @@ static void cleanup_subject(struct strbuf *subject) { char *pos; size_t remove; - int brackets_removed = 0; - while (subject->len) { switch (*subject->buf) { case 'r': case 'R': @@ -237,15 +235,10 @@ static void cleanup_subject(struct strbuf *subject) strbuf_remove(subject, 0, 1); continue; case '[': - /* remove only one set of square brackets */ - if (brackets_removed) - break; - if ((pos = strchr(subject->buf, ']'))) { remove = pos - subject->buf; if (remove <= (subject->len - remove) * 2) { strbuf_remove(subject, 0, remove + 1); - brackets_removed = 1; continue; } } else diff --git a/t/t5100/info0012 b/t/t5100/info0012 index 9cd1415d25..ac1216ff75 100644 --- a/t/t5100/info0012 +++ b/t/t5100/info0012 @@ -1,5 +1,5 @@ Author: Dmitriy Blinov Email: bda@mnsspb.ru -Subject: [Navy-patches] Изменён список пакетов необходимых для сборки +Subject: Изменён список пакетов необходимых для сборки Date: Wed, 12 Nov 2008 17:54:41 +0300 diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox index 3a757d7d8f..c3074ac573 100644 --- a/t/t5100/sample.mbox +++ b/t/t5100/sample.mbox @@ -514,7 +514,7 @@ MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit -Subject: [PATCH] [Navy-patches] +Subject: [Navy-patches] [PATCH] =?utf-8?b?0JjQt9C80LXQvdGR0L0g0YHQv9C40YHQvtC6INC/0LA=?= =?utf-8?b?0LrQtdGC0L7QsiDQvdC10L7QsdGF0L7QtNC40LzRi9GFINC00LvRjyA=?= =?utf-8?b?0YHQsdC+0YDQutC4?=