{"thread":{"id":"13455","subject":"[PATCH 0/2] Soem core git optimizations","startedAt":"2008-05-09T16:04:03Z","lastAt":"2008-05-11T02:42:51Z","messageCount":5,"participants":["Linus Torvalds","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"76461","messageId":"alpine.LFD.1.10.0805090856350.3142@woody.linux-foundation.org","threadId":"13455","inReplyTo":null,"subject":"[PATCH 0/2] Soem core git optimizations","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-05-09T16:04:03Z","receivedAt":"2008-05-09T16:04:03Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nI notice that Junio merged my fnmatch-avoidance patch, but I have a few \nother optimizations that I track in my private tree that I have sent out \nbut probably didn't get much attention. They do matter from a performance \nangle, even if not as much as avoiding fnmatch did.\n\nThe first patch is obvious and pretty trivial: just moving lstat() calls \nup a bit in the call-chain, so that you don't end up unnecessarily doing \ntwo lstat() calls after each other.\n\nThe second patch is the symlink detection rewrite, which avoids a _lot_ of \nunnecessary lstat calls under some loads by not doing the lstat on the \ndirectory path entries over and over for each pathname. It's just a \nsingle-deep cache for \"last directory seen\" and \"last symlink seen\", and \nreplaces the old code that only cached the last symlink.\n\nDiffstat for the combined thing as follows:\n\n builtin-apply.c  |    2 +-\n builtin-commit.c |    6 ++-\n cache.h          |    4 ++-\n diff-lib.c       |   10 +++---\n read-cache.c     |   29 +++++++++++--------\n symlinks.c       |   82 ++++++++++++++++++++++++++++++++---------------------\n unpack-trees.c   |   12 +++----\n 7 files changed, 84 insertions(+), 61 deletions(-)\n\nand while this probably doesn't matter on most loads, the reason I'm \nre-sending is that I think it's pretty solid and core code. I've been \nrunning with both of these patches (and some others) rebased on top of \nJunio's tree for the last few weeks.\n\n\t\tLinus\n"},{"id":"76462","messageId":"alpine.LFD.1.10.0805090904100.3142@woody.linux-foundation.org","threadId":"13455","inReplyTo":"alpine.LFD.1.10.0805090856350.3142@woody.linux-foundation.org","subject":"[PATCH 1/2] Avoid some unnecessary lstat() calls","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-05-09T16:11:43Z","receivedAt":"2008-05-09T16:11:43Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThe commit sequence used to do\n\n\tif (file_exists(p->path))\n\t\tadd_file_to_cache(p->path, 0);\n\nwhere both \"file_exists()\" and \"add_file_to_cache()\" needed to do a \nlstat() on the path to do their work.\n\nThis cuts down 'lstat()' calls for the partial commit case by two \nfor each path we know about (because we do this twice per path).\n\nJust move the lstat() to the caller instead (that's all that \n\"file_exists()\" really does), and pass the stat information down to the \nadd_to_cache() function.\n\nThis essentially makes 'add_to_index()' the core function that adds a path \nto the index, getting the index pointer, the pathname and the stat \ninformation as arguments. There are then shorthand helper functions that \nuse this core function:\n\n - 'add_to_cache()' is just 'add_to_index()' with the default index\n\n - 'add_file_to_cache/index()' is the same, but does the lstat() call \n   itself, so you can pass just the pathname if you don't already have the \n   stat information available.\n\nSo old users of the 'add_file_to_xyzzy()' are essentially left unchanged, \nand this just exposes the more generic helper function that can take \nexisting stat information into account.\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\n builtin-commit.c |    6 ++++--\n cache.h          |    2 ++\n read-cache.c     |   29 +++++++++++++++++------------\n 3 files changed, 23 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 256181a..6433f86 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -175,9 +175,11 @@ static void add_remove_files(struct path_list *list)\n {\n \tint i;\n \tfor (i = 0; i < list->nr; i++) {\n+\t\tstruct stat st;\n \t\tstruct path_list_item *p = &(list->items[i]);\n-\t\tif (file_exists(p->path))\n-\t\t\tadd_file_to_cache(p->path, 0);\n+\n+\t\tif (!lstat(p->path, &st))\n+\t\t\tadd_to_cache(p->path, &st, 0);\n \t\telse\n \t\t\tremove_file_from_cache(p->path);\n \t}\ndiff --git a/cache.h b/cache.h\nindex 80a8842..4803b03 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -261,6 +261,7 @@ static inline void remove_name_hash(struct cache_entry *ce)\n #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))\n #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))\n #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))\n+#define add_to_cache(path, st, verbose) add_to_index(&the_index, (path), (st), (verbose))\n #define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))\n #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)\n #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))\n@@ -365,6 +366,7 @@ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int opt\n extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);\n extern int remove_index_entry_at(struct index_state *, int pos);\n extern int remove_file_from_index(struct index_state *, const char *path);\n+extern int add_to_index(struct index_state *, const char *path, struct stat *, int verbose);\n extern int add_file_to_index(struct index_state *, const char *path, int verbose);\n extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);\n extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);\ndiff --git a/read-cache.c b/read-cache.c\nindex e71c3b7..f0d74b3 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -461,21 +461,18 @@ static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_\n \treturn new;\n }\n \n-int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n+int add_to_index(struct index_state *istate, const char *path, struct stat *st, int verbose)\n {\n \tint size, namelen;\n-\tstruct stat st;\n+\tmode_t st_mode = st->st_mode;\n \tstruct cache_entry *ce, *alias;\n \tunsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;\n \n-\tif (lstat(path, &st))\n-\t\tdie(\"%s: unable to stat (%s)\", path, strerror(errno));\n-\n-\tif (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))\n+\tif (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))\n \t\tdie(\"%s: can only add regular files, symbolic links or git-directories\", path);\n \n \tnamelen = strlen(path);\n-\tif (S_ISDIR(st.st_mode)) {\n+\tif (S_ISDIR(st_mode)) {\n \t\twhile (namelen && path[namelen-1] == '/')\n \t\t\tnamelen--;\n \t}\n@@ -483,10 +480,10 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n \tce = xcalloc(1, size);\n \tmemcpy(ce->name, path, namelen);\n \tce->ce_flags = namelen;\n-\tfill_stat_cache_info(ce, &st);\n+\tfill_stat_cache_info(ce, st);\n \n \tif (trust_executable_bit && has_symlinks)\n-\t\tce->ce_mode = create_ce_mode(st.st_mode);\n+\t\tce->ce_mode = create_ce_mode(st_mode);\n \telse {\n \t\t/* If there is an existing entry, pick the mode bits and type\n \t\t * from it, otherwise assume unexecutable regular file.\n@@ -495,18 +492,18 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n \t\tint pos = index_name_pos_also_unmerged(istate, path, namelen);\n \n \t\tent = (0 <= pos) ? istate->cache[pos] : NULL;\n-\t\tce->ce_mode = ce_mode_from_stat(ent, st.st_mode);\n+\t\tce->ce_mode = ce_mode_from_stat(ent, st_mode);\n \t}\n \n \talias = index_name_exists(istate, ce->name, ce_namelen(ce), ignore_case);\n-\tif (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, &st, ce_option)) {\n+\tif (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) {\n \t\t/* Nothing changed, really */\n \t\tfree(ce);\n \t\tce_mark_uptodate(alias);\n \t\talias->ce_flags |= CE_ADDED;\n \t\treturn 0;\n \t}\n-\tif (index_path(ce->sha1, path, &st, 1))\n+\tif (index_path(ce->sha1, path, st, 1))\n \t\tdie(\"unable to index file %s\", path);\n \tif (ignore_case && alias && different_name(ce, alias))\n \t\tce = create_alias_ce(ce, alias);\n@@ -518,6 +515,14 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n \treturn 0;\n }\n \n+int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n+{\n+\tstruct stat st;\n+\tif (lstat(path, &st))\n+\t\tdie(\"%s: unable to stat (%s)\", path, strerror(errno));\n+\treturn add_to_index(istate, path, &st, verbose);\n+}\n+\n struct cache_entry *make_cache_entry(unsigned int mode,\n \t\tconst unsigned char *sha1, const char *path, int stage,\n \t\tint refresh)\n"},{"id":"76464","messageId":"alpine.LFD.1.10.0805090911480.3142@woody.linux-foundation.org","threadId":"13455","inReplyTo":"alpine.LFD.1.10.0805090904100.3142@woody.linux-foundation.org","subject":"[PATCH 2/2] Optimize symlink/directory detection","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-05-09T16:21:07Z","receivedAt":"2008-05-09T16:21:07Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis is the base for making symlink detection in the middle fo a pathname \nsaner and (much) more efficient.\n\nUnder various loads, we want to verify that the full path leading up to a \nfilename is a real directory tree, and that when we successfully do an \n'lstat()' on a filename, we don't get a false positive due to a symlink in \nthe middle of the path that git should have seen as a symlink, not as a \nnormal path component.\n\nThe 'has_symlink_leading_path()' function already did this, and cached \na single level of symlink information, but didn't cache the _lack_ of a \nsymlink, so the normal behaviour was actually the wrong way around, and we \nended up doing an 'lstat()' on each path component to check that it was a \nreal directory.\n\nThis caches the last detected full directory and symlink entries, and \nspeeds up especially deep directory structures a lot by avoiding to \nlstat() all the directories leading up to each entry in the index.\n    \n[ This can - and should - probably be extended upon so that we eventually \n  never do a bare 'lstat()' on any path entries at *all* when checking the \n  index, but always check the full path carefully. Right now we do not \n  generally check the whole path for all our normal quick index \n  revalidation.\n\n  We should also make sure that we're careful about all the invalidation, \n  ie when we remove a link and replace it by a directory we should \n  invalidate the symlink cache if it matches (and vice versa for the \n  directory cache).\n\n  But regardless, the basic function needs to be sane to do that. The old\n  'has_symlink_leading_path()' was not capable enough - or indeed the code\n  readable enough - to really do that sanely. So I'm pushing this as not \n  just an optimization, but as a base for further work. ]\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\n\n builtin-apply.c |    2 +-\n cache.h         |    2 +-\n diff-lib.c      |   10 +++---\n symlinks.c      |   82 +++++++++++++++++++++++++++++++++----------------------\n unpack-trees.c  |   12 +++-----\n 5 files changed, 61 insertions(+), 47 deletions(-)\n\ndiff --git a/builtin-apply.c b/builtin-apply.c\nindex caa3f2a..1103625 100644\n--- a/builtin-apply.c\n+++ b/builtin-apply.c\n@@ -2247,7 +2247,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)\n \t\t * In such a case, path \"new_name\" does not exist as\n \t\t * far as git is concerned.\n \t\t */\n-\t\tif (has_symlink_leading_path(new_name, NULL))\n+\t\tif (has_symlink_leading_path(strlen(new_name), new_name))\n \t\t\treturn 0;\n \n \t\treturn error(\"%s: already exists in working directory\", new_name);\ndiff --git a/cache.h b/cache.h\nindex 4803b03..0d9d9e2 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -598,7 +598,7 @@ struct checkout {\n };\n \n extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);\n-extern int has_symlink_leading_path(const char *name, char *last_symlink);\n+extern int has_symlink_leading_path(int len, const char *name);\n \n extern struct alternate_object_database {\n \tstruct alternate_object_database *next;\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 9139e45..9966d99 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -341,14 +341,14 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)\n  * See if work tree has an entity that can be staged.  Return 0 if so,\n  * return 1 if not and return -1 if error.\n  */\n-static int check_work_tree_entity(const struct cache_entry *ce, struct stat *st, char *symcache)\n+static int check_work_tree_entity(const struct cache_entry *ce, struct stat *st)\n {\n \tif (lstat(ce->name, st) < 0) {\n \t\tif (errno != ENOENT && errno != ENOTDIR)\n \t\t\treturn -1;\n \t\treturn 1;\n \t}\n-\tif (has_symlink_leading_path(ce->name, symcache))\n+\tif (has_symlink_leading_path(ce_namelen(ce), ce->name))\n \t\treturn 1;\n \tif (S_ISDIR(st->st_mode)) {\n \t\tunsigned char sub[20];\n@@ -402,7 +402,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)\n \t\t\tmemset(&(dpath->parent[0]), 0,\n \t\t\t       sizeof(struct combine_diff_parent)*5);\n \n-\t\t\tchanged = check_work_tree_entity(ce, &st, symcache);\n+\t\t\tchanged = check_work_tree_entity(ce, &st);\n \t\t\tif (!changed)\n \t\t\t\tdpath->mode = ce_mode_from_stat(ce, st.st_mode);\n \t\t\telse {\n@@ -466,7 +466,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)\n \t\tif (ce_uptodate(ce))\n \t\t\tcontinue;\n \n-\t\tchanged = check_work_tree_entity(ce, &st, symcache);\n+\t\tchanged = check_work_tree_entity(ce, &st);\n \t\tif (changed) {\n \t\t\tif (changed < 0) {\n \t\t\t\tperror(ce->name);\n@@ -527,7 +527,7 @@ static int get_stat_data(struct cache_entry *ce,\n \tif (!cached) {\n \t\tint changed;\n \t\tstruct stat st;\n-\t\tchanged = check_work_tree_entity(ce, &st, cbdata->symcache);\n+\t\tchanged = check_work_tree_entity(ce, &st);\n \t\tif (changed < 0)\n \t\t\treturn -1;\n \t\telse if (changed) {\ndiff --git a/symlinks.c b/symlinks.c\nindex be9ace6..5a5e781 100644\n--- a/symlinks.c\n+++ b/symlinks.c\n@@ -1,48 +1,64 @@\n #include \"cache.h\"\n \n-int has_symlink_leading_path(const char *name, char *last_symlink)\n-{\n+struct pathname {\n+\tint len;\n \tchar path[PATH_MAX];\n-\tconst char *sp, *ep;\n-\tchar *dp;\n-\n-\tsp = name;\n-\tdp = path;\n-\n-\tif (last_symlink && *last_symlink) {\n-\t\tsize_t last_len = strlen(last_symlink);\n-\t\tsize_t len = strlen(name);\n-\t\tif (last_len < len &&\n-\t\t    !strncmp(name, last_symlink, last_len) &&\n-\t\t    name[last_len] == '/')\n-\t\t\treturn 1;\n-\t\t*last_symlink = '\\0';\n+};\n+\n+/* Return matching pathname prefix length, or zero if not matching */\n+static inline int match_pathname(int len, const char *name, struct pathname *match)\n+{\n+\tint match_len = match->len;\n+\treturn (len > match_len &&\n+\t\tname[match_len] == '/' &&\n+\t\t!memcmp(name, match->path, match_len)) ? match_len : 0;\n+}\n+\n+static inline void set_pathname(int len, const char *name, struct pathname *match)\n+{\n+\tif (len < PATH_MAX) {\n+\t\tmatch->len = len;\n+\t\tmemcpy(match->path, name, len);\n+\t\tmatch->path[len] = 0;\n \t}\n+}\n+\n+int has_symlink_leading_path(int len, const char *name)\n+{\n+\tstatic struct pathname link, nonlink;\n+\tchar path[PATH_MAX];\n+\tstruct stat st;\n+\tchar *sp;\n+\tint known_dir;\n \n-\twhile (1) {\n-\t\tsize_t len;\n-\t\tstruct stat st;\n+\t/*\n+\t * See if the last known symlink cache matches.\n+\t */\n+\tif (match_pathname(len, name, &link))\n+\t\treturn 1;\n \n-\t\tep = strchr(sp, '/');\n-\t\tif (!ep)\n-\t\t\tbreak;\n-\t\tlen = ep - sp;\n-\t\tif (PATH_MAX <= dp + len - path + 2)\n-\t\t\treturn 0; /* new name is longer than that??? */\n-\t\tmemcpy(dp, sp, len);\n-\t\tdp[len] = 0;\n+\t/*\n+\t * Get rid of the last known directory part\n+\t */\n+\tknown_dir = match_pathname(len, name, &nonlink);\n+\n+\twhile ((sp = strchr(name + known_dir + 1, '/')) != NULL) {\n+\t\tint thislen = sp - name ;\n+\t\tmemcpy(path, name, thislen);\n+\t\tpath[thislen] = 0;\n \n \t\tif (lstat(path, &st))\n \t\t\treturn 0;\n+\t\tif (S_ISDIR(st.st_mode)) {\n+\t\t\tset_pathname(thislen, path, &nonlink);\n+\t\t\tknown_dir = thislen;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (S_ISLNK(st.st_mode)) {\n-\t\t\tif (last_symlink)\n-\t\t\t\tstrcpy(last_symlink, path);\n+\t\t\tset_pathname(thislen, path, &link);\n \t\t\treturn 1;\n \t\t}\n-\n-\t\tdp[len++] = '/';\n-\t\tdp = dp + len;\n-\t\tsp = ep + 1;\n+\t\tbreak;\n \t}\n \treturn 0;\n }\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex feae846..1ab28fd 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -26,11 +26,12 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,\n  * directories, in case this unlink is the removal of the\n  * last entry in the directory -- empty directories are removed.\n  */\n-static void unlink_entry(char *name, char *last_symlink)\n+static void unlink_entry(struct cache_entry *ce)\n {\n \tchar *cp, *prev;\n+\tchar *name = ce->name;\n \n-\tif (has_symlink_leading_path(name, last_symlink))\n+\tif (has_symlink_leading_path(ce_namelen(ce), ce->name))\n \t\treturn;\n \tif (unlink(name))\n \t\treturn;\n@@ -58,7 +59,6 @@ static int check_updates(struct unpack_trees_options *o)\n {\n \tunsigned cnt = 0, total = 0;\n \tstruct progress *progress = NULL;\n-\tchar last_symlink[PATH_MAX];\n \tstruct index_state *index = &o->result;\n \tint i;\n \tint errs = 0;\n@@ -75,14 +75,13 @@ static int check_updates(struct unpack_trees_options *o)\n \t\tcnt = 0;\n \t}\n \n-\t*last_symlink = '\\0';\n \tfor (i = 0; i < index->cache_nr; i++) {\n \t\tstruct cache_entry *ce = index->cache[i];\n \n \t\tif (ce->ce_flags & CE_REMOVE) {\n \t\t\tdisplay_progress(progress, ++cnt);\n \t\t\tif (o->update)\n-\t\t\t\tunlink_entry(ce->name, last_symlink);\n+\t\t\t\tunlink_entry(ce);\n \t\t\tremove_index_entry_at(&o->result, i);\n \t\t\ti--;\n \t\t\tcontinue;\n@@ -97,7 +96,6 @@ static int check_updates(struct unpack_trees_options *o)\n \t\t\tce->ce_flags &= ~CE_UPDATE;\n \t\t\tif (o->update) {\n \t\t\t\terrs |= checkout_entry(ce, &state, NULL);\n-\t\t\t\t*last_symlink = '\\0';\n \t\t\t}\n \t\t}\n \t}\n@@ -553,7 +551,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,\n \tif (o->index_only || o->reset || !o->update)\n \t\treturn 0;\n \n-\tif (has_symlink_leading_path(ce->name, NULL))\n+\tif (has_symlink_leading_path(ce_namelen(ce), ce->name))\n \t\treturn 0;\n \n \tif (!lstat(ce->name, &st)) {\n"},{"id":"76571","messageId":"7v4p958t35.fsf@gitster.siamese.dyndns.org","threadId":"13455","inReplyTo":"alpine.LFD.1.10.0805090856350.3142@woody.linux-foundation.org","subject":"Re: [PATCH 0/2] Soem core git optimizations","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-05-11T02:36:46Z","receivedAt":"2008-05-11T02:36:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> I notice that Junio merged my fnmatch-avoidance patch, but I have a few \n> other optimizations that I track in my private tree that I have sent out \n> but probably didn't get much attention. They do matter from a performance \n> angle, even if not as much as avoiding fnmatch did.\n> ...\n> and while this probably doesn't matter on most loads, the reason I'm \n> re-sending is that I think it's pretty solid and core code. I've been \n> running with both of these patches (and some others) rebased on top of \n> Junio's tree for the last few weeks.\n\nUnfortunately this seems to depend on stuff still in 'next', and a trivial\nrebasing seems to break switching branches with \"git checkout\" in t4122\n(switching from 'master' to 'test' will lose arch/i386/boot/Makefile).\n\nI'll take a hint and merge the \"case insensitive\" and \"diff-submodule\"\nseries to 'master' and then apply these two --- the result does not have\nthe git-checkout breakage.\n"},{"id":"76572","messageId":"alpine.LFD.1.10.0805101940490.3142@woody.linux-foundation.org","threadId":"13455","inReplyTo":"7v4p958t35.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 0/2] Soem core git optimizations","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-05-11T02:42:51Z","receivedAt":"2008-05-11T02:42:51Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 10 May 2008, Junio C Hamano wrote:\n> \n> Unfortunately this seems to depend on stuff still in 'next', and a trivial\n> rebasing seems to break switching branches with \"git checkout\" in t4122\n> (switching from 'master' to 'test' will lose arch/i386/boot/Makefile).\n\nGaah. It's actually based on 'master', not 'next', but yeah, I have my \nother patch-series in my tree (the case-insensitivity ones you have in \nnext), and while I thought it was independent, I was wrong. Sorry about \nthat.\n\n\t\tLinus\n"}]}