2023-04-22 20:17:23 +00:00
|
|
|
#include "git-compat-util.h"
|
2006-03-30 06:55:43 +00:00
|
|
|
#include "tree-walk.h"
|
2010-12-15 15:02:44 +00:00
|
|
|
#include "dir.h"
|
2023-03-21 06:25:54 +00:00
|
|
|
#include "gettext.h"
|
2023-02-24 00:09:27 +00:00
|
|
|
#include "hex.h"
|
2023-04-11 07:41:53 +00:00
|
|
|
#include "object-file.h"
|
2023-05-16 06:34:06 +00:00
|
|
|
#include "object-store-ll.h"
|
2023-04-11 03:00:38 +00:00
|
|
|
#include "trace2.h"
|
2006-04-02 12:44:09 +00:00
|
|
|
#include "tree.h"
|
2013-07-14 08:35:25 +00:00
|
|
|
#include "pathspec.h"
|
2021-01-04 03:09:10 +00:00
|
|
|
#include "json-writer.h"
|
2023-08-31 06:21:40 +00:00
|
|
|
#include "environment.h"
|
2006-03-30 06:55:43 +00:00
|
|
|
|
2007-03-21 17:09:56 +00:00
|
|
|
static const char *get_mode(const char *str, unsigned int *modep)
|
|
|
|
{
|
|
|
|
unsigned char c;
|
|
|
|
unsigned int mode = 0;
|
|
|
|
|
2008-01-06 17:21:10 +00:00
|
|
|
if (*str == ' ')
|
|
|
|
return NULL;
|
|
|
|
|
2007-03-21 17:09:56 +00:00
|
|
|
while ((c = *str++) != ' ') {
|
|
|
|
if (c < '0' || c > '7')
|
|
|
|
return NULL;
|
|
|
|
mode = (mode << 3) + (c - '0');
|
|
|
|
}
|
|
|
|
*modep = mode;
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2016-09-27 20:59:51 +00:00
|
|
|
static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err)
|
2007-03-21 17:09:56 +00:00
|
|
|
{
|
|
|
|
const char *path;
|
|
|
|
unsigned int mode, len;
|
2018-07-16 01:27:54 +00:00
|
|
|
const unsigned hashsz = the_hash_algo->rawsz;
|
2007-03-21 17:09:56 +00:00
|
|
|
|
2018-07-16 01:27:54 +00:00
|
|
|
if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
|
2016-09-27 20:59:51 +00:00
|
|
|
strbuf_addstr(err, _("too-short tree object"));
|
|
|
|
return -1;
|
|
|
|
}
|
2008-01-06 17:21:10 +00:00
|
|
|
|
2007-03-21 17:09:56 +00:00
|
|
|
path = get_mode(buf, &mode);
|
2016-09-27 20:59:51 +00:00
|
|
|
if (!path) {
|
|
|
|
strbuf_addstr(err, _("malformed mode in tree entry"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!*path) {
|
|
|
|
strbuf_addstr(err, _("empty filename in tree entry"));
|
|
|
|
return -1;
|
|
|
|
}
|
2007-03-21 17:09:56 +00:00
|
|
|
len = strlen(path) + 1;
|
|
|
|
|
|
|
|
/* Initialize the descriptor entry */
|
|
|
|
desc->entry.path = path;
|
tree-walk: add a mechanism for getting non-canonicalized modes
When using init_tree_desc() and tree_entry() to iterate over a tree, we
always canonicalize the modes coming out of the tree. This is a good
thing to prevent bugs or oddities in normal code paths, but it's
counter-productive for tools like fsck that want to see the exact
contents.
We can address this by adding an option to avoid the extra
canonicalization. A few notes on the implementation:
- I've attached the new option to the tree_desc struct itself. The
actual code change is in decode_tree_entry(), which is in turn
called by the public update_tree_entry(), tree_entry(), and
init_tree_desc() functions, plus their "gently" counterparts.
By letting it ride along in the struct, we can avoid changing the
signature of those functions, which are called many times. Plus it's
conceptually simpler: you really want a particular iteration of a
tree to be "raw" or not, rather than individual calls.
- We still have to set the new option somewhere. The struct is
initialized by init_tree_desc(). I added the new flags field only to
the "gently" version. That avoids disturbing the much more numerous
non-gentle callers, and it makes sense that anybody being careful
about looking at raw modes would also be careful about bogus trees
(i.e., the caller will be something like fsck in the first place).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-08-10 21:01:17 +00:00
|
|
|
desc->entry.mode = (desc->flags & TREE_DESC_RAW_MODES) ? mode : canon_mode(mode);
|
2019-01-15 00:39:44 +00:00
|
|
|
desc->entry.pathlen = len - 1;
|
2021-04-26 01:02:50 +00:00
|
|
|
oidread(&desc->entry.oid, (const unsigned char *)path + len);
|
2016-09-27 20:59:51 +00:00
|
|
|
|
|
|
|
return 0;
|
2007-03-21 17:09:56 +00:00
|
|
|
}
|
|
|
|
|
tree-walk: add a mechanism for getting non-canonicalized modes
When using init_tree_desc() and tree_entry() to iterate over a tree, we
always canonicalize the modes coming out of the tree. This is a good
thing to prevent bugs or oddities in normal code paths, but it's
counter-productive for tools like fsck that want to see the exact
contents.
We can address this by adding an option to avoid the extra
canonicalization. A few notes on the implementation:
- I've attached the new option to the tree_desc struct itself. The
actual code change is in decode_tree_entry(), which is in turn
called by the public update_tree_entry(), tree_entry(), and
init_tree_desc() functions, plus their "gently" counterparts.
By letting it ride along in the struct, we can avoid changing the
signature of those functions, which are called many times. Plus it's
conceptually simpler: you really want a particular iteration of a
tree to be "raw" or not, rather than individual calls.
- We still have to set the new option somewhere. The struct is
initialized by init_tree_desc(). I added the new flags field only to
the "gently" version. That avoids disturbing the much more numerous
non-gentle callers, and it makes sense that anybody being careful
about looking at raw modes would also be careful about bogus trees
(i.e., the caller will be something like fsck in the first place).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-08-10 21:01:17 +00:00
|
|
|
static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
|
|
|
|
unsigned long size, struct strbuf *err,
|
|
|
|
enum tree_desc_flags flags)
|
2007-03-21 17:08:25 +00:00
|
|
|
{
|
|
|
|
desc->buffer = buffer;
|
|
|
|
desc->size = size;
|
tree-walk: add a mechanism for getting non-canonicalized modes
When using init_tree_desc() and tree_entry() to iterate over a tree, we
always canonicalize the modes coming out of the tree. This is a good
thing to prevent bugs or oddities in normal code paths, but it's
counter-productive for tools like fsck that want to see the exact
contents.
We can address this by adding an option to avoid the extra
canonicalization. A few notes on the implementation:
- I've attached the new option to the tree_desc struct itself. The
actual code change is in decode_tree_entry(), which is in turn
called by the public update_tree_entry(), tree_entry(), and
init_tree_desc() functions, plus their "gently" counterparts.
By letting it ride along in the struct, we can avoid changing the
signature of those functions, which are called many times. Plus it's
conceptually simpler: you really want a particular iteration of a
tree to be "raw" or not, rather than individual calls.
- We still have to set the new option somewhere. The struct is
initialized by init_tree_desc(). I added the new flags field only to
the "gently" version. That avoids disturbing the much more numerous
non-gentle callers, and it makes sense that anybody being careful
about looking at raw modes would also be careful about bogus trees
(i.e., the caller will be something like fsck in the first place).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-08-10 21:01:17 +00:00
|
|
|
desc->flags = flags;
|
2007-03-21 17:09:56 +00:00
|
|
|
if (size)
|
2016-09-27 20:59:51 +00:00
|
|
|
return decode_tree_entry(desc, buffer, size, err);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
|
|
|
|
{
|
|
|
|
struct strbuf err = STRBUF_INIT;
|
tree-walk: add a mechanism for getting non-canonicalized modes
When using init_tree_desc() and tree_entry() to iterate over a tree, we
always canonicalize the modes coming out of the tree. This is a good
thing to prevent bugs or oddities in normal code paths, but it's
counter-productive for tools like fsck that want to see the exact
contents.
We can address this by adding an option to avoid the extra
canonicalization. A few notes on the implementation:
- I've attached the new option to the tree_desc struct itself. The
actual code change is in decode_tree_entry(), which is in turn
called by the public update_tree_entry(), tree_entry(), and
init_tree_desc() functions, plus their "gently" counterparts.
By letting it ride along in the struct, we can avoid changing the
signature of those functions, which are called many times. Plus it's
conceptually simpler: you really want a particular iteration of a
tree to be "raw" or not, rather than individual calls.
- We still have to set the new option somewhere. The struct is
initialized by init_tree_desc(). I added the new flags field only to
the "gently" version. That avoids disturbing the much more numerous
non-gentle callers, and it makes sense that anybody being careful
about looking at raw modes would also be careful about bogus trees
(i.e., the caller will be something like fsck in the first place).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-08-10 21:01:17 +00:00
|
|
|
if (init_tree_desc_internal(desc, buffer, size, &err, 0))
|
2016-09-27 20:59:51 +00:00
|
|
|
die("%s", err.buf);
|
|
|
|
strbuf_release(&err);
|
|
|
|
}
|
|
|
|
|
tree-walk: add a mechanism for getting non-canonicalized modes
When using init_tree_desc() and tree_entry() to iterate over a tree, we
always canonicalize the modes coming out of the tree. This is a good
thing to prevent bugs or oddities in normal code paths, but it's
counter-productive for tools like fsck that want to see the exact
contents.
We can address this by adding an option to avoid the extra
canonicalization. A few notes on the implementation:
- I've attached the new option to the tree_desc struct itself. The
actual code change is in decode_tree_entry(), which is in turn
called by the public update_tree_entry(), tree_entry(), and
init_tree_desc() functions, plus their "gently" counterparts.
By letting it ride along in the struct, we can avoid changing the
signature of those functions, which are called many times. Plus it's
conceptually simpler: you really want a particular iteration of a
tree to be "raw" or not, rather than individual calls.
- We still have to set the new option somewhere. The struct is
initialized by init_tree_desc(). I added the new flags field only to
the "gently" version. That avoids disturbing the much more numerous
non-gentle callers, and it makes sense that anybody being careful
about looking at raw modes would also be careful about bogus trees
(i.e., the caller will be something like fsck in the first place).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-08-10 21:01:17 +00:00
|
|
|
int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned long size,
|
|
|
|
enum tree_desc_flags flags)
|
2016-09-27 20:59:51 +00:00
|
|
|
{
|
|
|
|
struct strbuf err = STRBUF_INIT;
|
tree-walk: add a mechanism for getting non-canonicalized modes
When using init_tree_desc() and tree_entry() to iterate over a tree, we
always canonicalize the modes coming out of the tree. This is a good
thing to prevent bugs or oddities in normal code paths, but it's
counter-productive for tools like fsck that want to see the exact
contents.
We can address this by adding an option to avoid the extra
canonicalization. A few notes on the implementation:
- I've attached the new option to the tree_desc struct itself. The
actual code change is in decode_tree_entry(), which is in turn
called by the public update_tree_entry(), tree_entry(), and
init_tree_desc() functions, plus their "gently" counterparts.
By letting it ride along in the struct, we can avoid changing the
signature of those functions, which are called many times. Plus it's
conceptually simpler: you really want a particular iteration of a
tree to be "raw" or not, rather than individual calls.
- We still have to set the new option somewhere. The struct is
initialized by init_tree_desc(). I added the new flags field only to
the "gently" version. That avoids disturbing the much more numerous
non-gentle callers, and it makes sense that anybody being careful
about looking at raw modes would also be careful about bogus trees
(i.e., the caller will be something like fsck in the first place).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-08-10 21:01:17 +00:00
|
|
|
int result = init_tree_desc_internal(desc, buffer, size, &err, flags);
|
2016-09-27 20:59:51 +00:00
|
|
|
if (result)
|
|
|
|
error("%s", err.buf);
|
|
|
|
strbuf_release(&err);
|
|
|
|
return result;
|
2007-03-21 17:08:25 +00:00
|
|
|
}
|
|
|
|
|
2019-06-27 09:28:48 +00:00
|
|
|
void *fill_tree_descriptor(struct repository *r,
|
|
|
|
struct tree_desc *desc,
|
|
|
|
const struct object_id *oid)
|
2006-03-30 06:55:43 +00:00
|
|
|
{
|
|
|
|
unsigned long size = 0;
|
|
|
|
void *buf = NULL;
|
|
|
|
|
2017-08-12 08:32:59 +00:00
|
|
|
if (oid) {
|
2022-02-04 23:48:34 +00:00
|
|
|
buf = read_object_with_reference(r, oid, OBJ_TREE, &size, NULL);
|
2006-03-30 06:55:43 +00:00
|
|
|
if (!buf)
|
2017-08-12 08:32:59 +00:00
|
|
|
die("unable to read tree %s", oid_to_hex(oid));
|
2006-03-30 06:55:43 +00:00
|
|
|
}
|
2007-03-21 17:08:25 +00:00
|
|
|
init_tree_desc(desc, buf, size);
|
2006-03-30 06:55:43 +00:00
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void entry_clear(struct name_entry *a)
|
|
|
|
{
|
|
|
|
memset(a, 0, sizeof(*a));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void entry_extract(struct tree_desc *t, struct name_entry *a)
|
|
|
|
{
|
2007-03-21 17:09:56 +00:00
|
|
|
*a = t->entry;
|
2006-03-30 06:55:43 +00:00
|
|
|
}
|
|
|
|
|
2016-09-27 20:59:51 +00:00
|
|
|
static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err)
|
2006-03-30 06:55:43 +00:00
|
|
|
{
|
2007-03-21 17:08:25 +00:00
|
|
|
const void *buf = desc->buffer;
|
2019-01-15 00:39:44 +00:00
|
|
|
const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + the_hash_algo->rawsz;
|
2006-03-30 06:55:43 +00:00
|
|
|
unsigned long size = desc->size;
|
2007-03-21 17:09:56 +00:00
|
|
|
unsigned long len = end - (const unsigned char *)buf;
|
2006-03-30 06:55:43 +00:00
|
|
|
|
|
|
|
if (size < len)
|
2016-09-27 20:59:50 +00:00
|
|
|
die(_("too-short tree file"));
|
2007-03-21 17:09:56 +00:00
|
|
|
buf = end;
|
|
|
|
size -= len;
|
|
|
|
desc->buffer = buf;
|
|
|
|
desc->size = size;
|
|
|
|
if (size)
|
2016-09-27 20:59:51 +00:00
|
|
|
return decode_tree_entry(desc, buf, size, err);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void update_tree_entry(struct tree_desc *desc)
|
|
|
|
{
|
|
|
|
struct strbuf err = STRBUF_INIT;
|
|
|
|
if (update_tree_entry_internal(desc, &err))
|
|
|
|
die("%s", err.buf);
|
|
|
|
strbuf_release(&err);
|
|
|
|
}
|
|
|
|
|
|
|
|
int update_tree_entry_gently(struct tree_desc *desc)
|
|
|
|
{
|
|
|
|
struct strbuf err = STRBUF_INIT;
|
|
|
|
if (update_tree_entry_internal(desc, &err)) {
|
|
|
|
error("%s", err.buf);
|
|
|
|
strbuf_release(&err);
|
|
|
|
/* Stop processing this tree after error */
|
|
|
|
desc->size = 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
strbuf_release(&err);
|
|
|
|
return 0;
|
2006-03-30 06:55:43 +00:00
|
|
|
}
|
|
|
|
|
tree_entry(): new tree-walking helper function
This adds a "tree_entry()" function that combines the common operation of
doing a "tree_entry_extract()" + "update_tree_entry()".
It also has a simplified calling convention, designed for simple loops
that traverse over a whole tree: the arguments are pointers to the tree
descriptor and a name_entry structure to fill in, and it returns a boolean
"true" if there was an entry left to be gotten in the tree.
This allows tree traversal with
struct tree_desc desc;
struct name_entry entry;
desc.buf = tree->buffer;
desc.size = tree->size;
while (tree_entry(&desc, &entry) {
... use "entry.{path, sha1, mode, pathlen}" ...
}
which is not only shorter than writing it out in full, it's hopefully less
error prone too.
[ It's actually a tad faster too - we don't need to recalculate the entry
pathlength in both extract and update, but need to do it only once.
Also, some callers can avoid doing a "strlen()" on the result, since
it's returned as part of the name_entry structure.
However, by now we're talking just 1% speedup on "git-rev-list --objects
--all", and we're definitely at the point where tree walking is no
longer the issue any more. ]
NOTE! Not everybody wants to use this new helper function, since some of
the tree walkers very much on purpose do the descriptor update separately
from the entry extraction. So the "extract + update" sequence still
remains as the core sequence, this is just a simplified interface.
We should probably add a silly two-line inline helper function for
initializing the descriptor from the "struct tree" too, just to cut down
on the noise from that common "desc" initializer.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-05-30 16:45:45 +00:00
|
|
|
int tree_entry(struct tree_desc *desc, struct name_entry *entry)
|
|
|
|
{
|
2007-03-21 17:09:56 +00:00
|
|
|
if (!desc->size)
|
tree_entry(): new tree-walking helper function
This adds a "tree_entry()" function that combines the common operation of
doing a "tree_entry_extract()" + "update_tree_entry()".
It also has a simplified calling convention, designed for simple loops
that traverse over a whole tree: the arguments are pointers to the tree
descriptor and a name_entry structure to fill in, and it returns a boolean
"true" if there was an entry left to be gotten in the tree.
This allows tree traversal with
struct tree_desc desc;
struct name_entry entry;
desc.buf = tree->buffer;
desc.size = tree->size;
while (tree_entry(&desc, &entry) {
... use "entry.{path, sha1, mode, pathlen}" ...
}
which is not only shorter than writing it out in full, it's hopefully less
error prone too.
[ It's actually a tad faster too - we don't need to recalculate the entry
pathlength in both extract and update, but need to do it only once.
Also, some callers can avoid doing a "strlen()" on the result, since
it's returned as part of the name_entry structure.
However, by now we're talking just 1% speedup on "git-rev-list --objects
--all", and we're definitely at the point where tree walking is no
longer the issue any more. ]
NOTE! Not everybody wants to use this new helper function, since some of
the tree walkers very much on purpose do the descriptor update separately
from the entry extraction. So the "extract + update" sequence still
remains as the core sequence, this is just a simplified interface.
We should probably add a silly two-line inline helper function for
initializing the descriptor from the "struct tree" too, just to cut down
on the noise from that common "desc" initializer.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-05-30 16:45:45 +00:00
|
|
|
return 0;
|
|
|
|
|
2007-03-21 17:09:56 +00:00
|
|
|
*entry = desc->entry;
|
|
|
|
update_tree_entry(desc);
|
tree_entry(): new tree-walking helper function
This adds a "tree_entry()" function that combines the common operation of
doing a "tree_entry_extract()" + "update_tree_entry()".
It also has a simplified calling convention, designed for simple loops
that traverse over a whole tree: the arguments are pointers to the tree
descriptor and a name_entry structure to fill in, and it returns a boolean
"true" if there was an entry left to be gotten in the tree.
This allows tree traversal with
struct tree_desc desc;
struct name_entry entry;
desc.buf = tree->buffer;
desc.size = tree->size;
while (tree_entry(&desc, &entry) {
... use "entry.{path, sha1, mode, pathlen}" ...
}
which is not only shorter than writing it out in full, it's hopefully less
error prone too.
[ It's actually a tad faster too - we don't need to recalculate the entry
pathlength in both extract and update, but need to do it only once.
Also, some callers can avoid doing a "strlen()" on the result, since
it's returned as part of the name_entry structure.
However, by now we're talking just 1% speedup on "git-rev-list --objects
--all", and we're definitely at the point where tree walking is no
longer the issue any more. ]
NOTE! Not everybody wants to use this new helper function, since some of
the tree walkers very much on purpose do the descriptor update separately
from the entry extraction. So the "extract + update" sequence still
remains as the core sequence, this is just a simplified interface.
We should probably add a silly two-line inline helper function for
initializing the descriptor from the "struct tree" too, just to cut down
on the noise from that common "desc" initializer.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-05-30 16:45:45 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-09-27 20:59:51 +00:00
|
|
|
int tree_entry_gently(struct tree_desc *desc, struct name_entry *entry)
|
|
|
|
{
|
|
|
|
if (!desc->size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
*entry = desc->entry;
|
|
|
|
if (update_tree_entry_gently(desc))
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-01-04 03:09:10 +00:00
|
|
|
static int traverse_trees_atexit_registered;
|
|
|
|
static int traverse_trees_count;
|
|
|
|
static int traverse_trees_cur_depth;
|
|
|
|
static int traverse_trees_max_depth;
|
|
|
|
|
|
|
|
static void trace2_traverse_trees_statistics_atexit(void)
|
|
|
|
{
|
|
|
|
struct json_writer jw = JSON_WRITER_INIT;
|
|
|
|
|
|
|
|
jw_object_begin(&jw, 0);
|
|
|
|
jw_object_intmax(&jw, "traverse_trees_count", traverse_trees_count);
|
|
|
|
jw_object_intmax(&jw, "traverse_trees_max_depth", traverse_trees_max_depth);
|
|
|
|
jw_end(&jw);
|
|
|
|
|
|
|
|
trace2_data_json("traverse_trees", the_repository, "statistics", &jw);
|
|
|
|
|
|
|
|
jw_release(&jw);
|
|
|
|
}
|
|
|
|
|
2008-03-06 02:59:29 +00:00
|
|
|
void setup_traverse_info(struct traverse_info *info, const char *base)
|
|
|
|
{
|
2019-07-31 04:38:18 +00:00
|
|
|
size_t pathlen = strlen(base);
|
2008-03-06 23:44:48 +00:00
|
|
|
static struct traverse_info dummy;
|
2008-03-06 02:59:29 +00:00
|
|
|
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
if (pathlen && base[pathlen-1] == '/')
|
|
|
|
pathlen--;
|
|
|
|
info->pathlen = pathlen ? pathlen + 1 : 0;
|
2019-07-31 04:38:15 +00:00
|
|
|
info->name = base;
|
|
|
|
info->namelen = pathlen;
|
setup_traverse_info(): stop copying oid
We assume that if setup_traverse_info() is passed a non-empty "base"
string, that string is pointing into a tree object and we can read the
object oid by skipping past the trailing NUL.
As it turns out, this is not true for either of the two calls, and we
may end up reading garbage bytes:
1. In git-merge-tree, our base string is either empty (in which case
we'd never run this code), or it comes from our traverse_path()
helper. The latter overallocates a buffer by the_hash_algo->rawsz
bytes, but then fills it with only make_traverse_path(), leaving
those extra bytes uninitialized (but part of a legitimate heap
buffer).
2. In unpack_trees(), we pass o->prefix, which is some arbitrary
string from the caller. In "git read-tree --prefix=foo", for
instance, it will point to the command-line parameter, and we'll
read 20 bytes past the end of the string.
Interestingly, tools like ASan do not detect (2) because the process
argv is part of a big pre-allocated buffer. So we're reading trash, but
it's trash that's probably part of the next argument, or the
environment.
You can convince it to fail by putting something like this at the
beginning of common-main.c's main() function:
{
int i;
for (i = 0; i < argc; i++)
argv[i] = xstrdup_or_null(argv[i]);
}
That puts the arguments into their own heap buffers, so running:
make SANITIZE=address test
will find problems when "read-tree --prefix" is used (e.g., in t3030).
Doubly interesting, even with the hackery above, this does not fail
prior to ea82b2a085 (tree-walk: store object_id in a separate member,
2019-01-15). That commit switched setup_traverse_info() to actually
copying the hash, rather than simply pointing to it. That pointer was
always pointing to garbage memory, but that commit started actually
dereferencing the bytes, which is what triggers ASan.
That also implies that nobody actually cares about reading these oid
bytes anyway (or at least no path covered by our tests). And manual
inspection of the code backs that up (I'll follow this patch with some
cleanups that show definitively this is the case, but they're quite
invasive, so it's worth doing this fix on its own).
So let's drop the bogus hashcpy(), along with the confusing oversizing
in merge-tree.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-07-31 04:38:11 +00:00
|
|
|
if (pathlen)
|
2008-03-06 23:44:48 +00:00
|
|
|
info->prev = &dummy;
|
2021-01-04 03:09:10 +00:00
|
|
|
|
|
|
|
if (trace2_is_enabled() && !traverse_trees_atexit_registered) {
|
|
|
|
atexit(trace2_traverse_trees_statistics_atexit);
|
|
|
|
traverse_trees_atexit_registered = 1;
|
|
|
|
}
|
2008-03-06 02:59:29 +00:00
|
|
|
}
|
|
|
|
|
2019-07-31 04:38:25 +00:00
|
|
|
char *make_traverse_path(char *path, size_t pathlen,
|
|
|
|
const struct traverse_info *info,
|
2019-07-31 04:38:15 +00:00
|
|
|
const char *name, size_t namelen)
|
2008-03-06 02:59:29 +00:00
|
|
|
{
|
2019-07-31 04:38:25 +00:00
|
|
|
/* Always points to the end of the name we're about to add */
|
|
|
|
size_t pos = st_add(info->pathlen, namelen);
|
2008-03-06 02:59:29 +00:00
|
|
|
|
2019-07-31 04:38:25 +00:00
|
|
|
if (pos >= pathlen)
|
|
|
|
BUG("too small buffer passed to make_traverse_path");
|
2008-03-06 02:59:29 +00:00
|
|
|
|
2019-07-31 04:38:25 +00:00
|
|
|
path[pos] = 0;
|
2008-03-06 02:59:29 +00:00
|
|
|
for (;;) {
|
2019-07-31 04:38:25 +00:00
|
|
|
if (pos < namelen)
|
|
|
|
BUG("traverse_info pathlen does not match strings");
|
|
|
|
pos -= namelen;
|
|
|
|
memcpy(path + pos, name, namelen);
|
|
|
|
|
|
|
|
if (!pos)
|
2008-03-06 02:59:29 +00:00
|
|
|
break;
|
2019-07-31 04:38:25 +00:00
|
|
|
path[--pos] = '/';
|
|
|
|
|
|
|
|
if (!info)
|
|
|
|
BUG("traverse_info ran out of list items");
|
2019-07-31 04:38:15 +00:00
|
|
|
name = info->name;
|
|
|
|
namelen = info->namelen;
|
2008-03-06 02:59:29 +00:00
|
|
|
info = info->prev;
|
|
|
|
}
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2019-07-31 04:38:23 +00:00
|
|
|
void strbuf_make_traverse_path(struct strbuf *out,
|
|
|
|
const struct traverse_info *info,
|
|
|
|
const char *name, size_t namelen)
|
|
|
|
{
|
|
|
|
size_t len = traverse_path_len(info, namelen);
|
|
|
|
|
|
|
|
strbuf_grow(out, len);
|
2019-07-31 04:38:25 +00:00
|
|
|
make_traverse_path(out->buf + out->len, out->alloc - out->len,
|
|
|
|
info, name, namelen);
|
2019-07-31 04:38:23 +00:00
|
|
|
strbuf_setlen(out, out->len + len);
|
|
|
|
}
|
|
|
|
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
struct tree_desc_skip {
|
|
|
|
struct tree_desc_skip *prev;
|
|
|
|
const void *ptr;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct tree_desc_x {
|
|
|
|
struct tree_desc d;
|
|
|
|
struct tree_desc_skip *skip;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int check_entry_match(const char *a, int a_len, const char *b, int b_len)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The caller wants to pick *a* from a tree or nothing.
|
|
|
|
* We are looking at *b* in a tree.
|
|
|
|
*
|
|
|
|
* (0) If a and b are the same name, we are trivially happy.
|
|
|
|
*
|
|
|
|
* There are three possibilities where *a* could be hiding
|
|
|
|
* behind *b*.
|
|
|
|
*
|
|
|
|
* (1) *a* == "t", *b* == "ab" i.e. *b* sorts earlier than *a* no
|
|
|
|
* matter what.
|
|
|
|
* (2) *a* == "t", *b* == "t-2" and "t" is a subtree in the tree;
|
|
|
|
* (3) *a* == "t-2", *b* == "t" and "t-2" is a blob in the tree.
|
|
|
|
*
|
|
|
|
* Otherwise we know *a* won't appear in the tree without
|
|
|
|
* scanning further.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int cmp = name_compare(a, a_len, b, b_len);
|
|
|
|
|
|
|
|
/* Most common case first -- reading sync'd trees */
|
|
|
|
if (!cmp)
|
|
|
|
return cmp;
|
|
|
|
|
|
|
|
if (0 < cmp) {
|
|
|
|
/* a comes after b; it does not matter if it is case (3)
|
|
|
|
if (b_len < a_len && !memcmp(a, b, b_len) && a[b_len] < '/')
|
|
|
|
return 1;
|
|
|
|
*/
|
|
|
|
return 1; /* keep looking */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* b comes after a; are we looking at case (2)? */
|
|
|
|
if (a_len < b_len && !memcmp(a, b, a_len) && b[a_len] < '/')
|
|
|
|
return 1; /* keep looking */
|
|
|
|
|
|
|
|
return -1; /* a cannot appear in the tree */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* From the extended tree_desc, extract the first name entry, while
|
|
|
|
* paying attention to the candidate "first" name. Most importantly,
|
|
|
|
* when looking for an entry, if there are entries that sorts earlier
|
|
|
|
* in the tree object representation than that name, skip them and
|
|
|
|
* process the named entry first. We will remember that we haven't
|
|
|
|
* processed the first entry yet, and in the later call skip the
|
|
|
|
* entry we processed early when update_extended_entry() is called.
|
|
|
|
*
|
|
|
|
* E.g. if the underlying tree object has these entries:
|
|
|
|
*
|
|
|
|
* blob "t-1"
|
|
|
|
* blob "t-2"
|
|
|
|
* tree "t"
|
|
|
|
* blob "t=1"
|
|
|
|
*
|
|
|
|
* and the "first" asks for "t", remember that we still need to
|
|
|
|
* process "t-1" and "t-2" but extract "t". After processing the
|
|
|
|
* entry "t" from this call, the caller will let us know by calling
|
|
|
|
* update_extended_entry() that we can remember "t" has been processed
|
|
|
|
* already.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void extended_entry_extract(struct tree_desc_x *t,
|
|
|
|
struct name_entry *a,
|
|
|
|
const char *first,
|
|
|
|
int first_len)
|
|
|
|
{
|
|
|
|
const char *path;
|
|
|
|
int len;
|
|
|
|
struct tree_desc probe;
|
|
|
|
struct tree_desc_skip *skip;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract the first entry from the tree_desc, but skip the
|
|
|
|
* ones that we already returned in earlier rounds.
|
|
|
|
*/
|
|
|
|
while (1) {
|
|
|
|
if (!t->d.size) {
|
|
|
|
entry_clear(a);
|
|
|
|
break; /* not found */
|
|
|
|
}
|
|
|
|
entry_extract(&t->d, a);
|
|
|
|
for (skip = t->skip; skip; skip = skip->prev)
|
|
|
|
if (a->path == skip->ptr)
|
|
|
|
break; /* found */
|
|
|
|
if (!skip)
|
|
|
|
break;
|
|
|
|
/* We have processed this entry already. */
|
|
|
|
update_tree_entry(&t->d);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!first || !a->path)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The caller wants "first" from this tree, or nothing.
|
|
|
|
*/
|
|
|
|
path = a->path;
|
2011-10-24 06:36:09 +00:00
|
|
|
len = tree_entry_len(a);
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
switch (check_entry_match(first, first_len, path, len)) {
|
|
|
|
case -1:
|
|
|
|
entry_clear(a);
|
|
|
|
case 0:
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to look-ahead -- we suspect that a subtree whose
|
|
|
|
* name is "first" may be hiding behind the current entry "path".
|
|
|
|
*/
|
|
|
|
probe = t->d;
|
|
|
|
while (probe.size) {
|
|
|
|
entry_extract(&probe, a);
|
|
|
|
path = a->path;
|
2011-10-24 06:36:09 +00:00
|
|
|
len = tree_entry_len(a);
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
switch (check_entry_match(first, first_len, path, len)) {
|
|
|
|
case -1:
|
|
|
|
entry_clear(a);
|
|
|
|
case 0:
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
update_tree_entry(&probe);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* keep looking */
|
|
|
|
}
|
|
|
|
entry_clear(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void update_extended_entry(struct tree_desc_x *t, struct name_entry *a)
|
|
|
|
{
|
|
|
|
if (t->d.entry.path == a->path) {
|
|
|
|
update_tree_entry(&t->d);
|
|
|
|
} else {
|
|
|
|
/* we have returned this entry early */
|
|
|
|
struct tree_desc_skip *skip = xmalloc(sizeof(*skip));
|
|
|
|
skip->ptr = a->path;
|
|
|
|
skip->prev = t->skip;
|
|
|
|
t->skip = skip;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_extended_entry(struct tree_desc_x *t)
|
|
|
|
{
|
|
|
|
struct tree_desc_skip *p, *s;
|
|
|
|
|
|
|
|
for (s = t->skip; s; s = p) {
|
|
|
|
p = s->prev;
|
|
|
|
free(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-18 16:47:57 +00:00
|
|
|
static inline int prune_traversal(struct index_state *istate,
|
|
|
|
struct name_entry *e,
|
2011-08-29 19:26:05 +00:00
|
|
|
struct traverse_info *info,
|
|
|
|
struct strbuf *base,
|
|
|
|
int still_interesting)
|
|
|
|
{
|
|
|
|
if (!info->pathspec || still_interesting == 2)
|
|
|
|
return 2;
|
|
|
|
if (still_interesting < 0)
|
|
|
|
return still_interesting;
|
2018-11-18 16:47:57 +00:00
|
|
|
return tree_entry_interesting(istate, e, base,
|
2023-07-07 22:21:15 +00:00
|
|
|
info->pathspec);
|
2011-08-29 19:26:05 +00:00
|
|
|
}
|
|
|
|
|
2018-11-18 16:47:57 +00:00
|
|
|
int traverse_trees(struct index_state *istate,
|
|
|
|
int n, struct tree_desc *t,
|
|
|
|
struct traverse_info *info)
|
2006-03-30 06:55:43 +00:00
|
|
|
{
|
2023-08-31 06:19:22 +00:00
|
|
|
int ret = 0;
|
tree-walk: reduce stack size for recursive functions
The traverse_trees() and traverse_trees_recursive() functions call each
other recursively. In a deep tree, this can result in running out of
stack space and crashing.
There's obviously going to be some limit here based on available stack,
but the problem is exacerbated by a few large structs, many of which we
over-allocate. For example, in traverse_trees() we store a name_entry
and tree_desc_x per tree, both of which contain an object_id (which is
now 32 bytes). And we allocate 8 of them (from MAX_TRAVERSE_TREES), even
though many traversals will only look at 1 or 2.
Interestingly, we used to allocate these on the heap, prior to
8dd40c0472 (traverse_trees(): use stack array for name entries,
2020-01-30). That commit was trying to simplify away allocation size
computations, and naively assumed that the sizes were small enough not
to matter. And they don't in normal cases, but on my stock Debian system
I see a crash running "git archive" on a tree with ~3600 entries.
That's deep enough we wouldn't see it in practice, but probably shallow
enough that we'd prefer not to make it a hard limit. Especially because
other systems may have even smaller stacks.
We can replace these stack variables with a few malloc invocations. This
reduces the stack sizes for the two functions from 1128 and 752 bytes,
respectively, down to 40 and 92 bytes. That allows a depth of ~13000 on
my machine (the improvement isn't in linear proportion because my
numbers don't count the size of parameters and other function overhead).
The possible downsides are:
1. We now have to remember to free(). But both functions have an easy
single exit (and already had to clean up other bits anyway).
2. The extra malloc()/free() overhead might be measurable. I tested
this by setting up a 3000-depth tree with a single blob and running
"git archive" on it. After switching to the heap, it consistently
runs 2-3% faster! Presumably this is because the 1K+ of wasted
stack space penalized memory caches.
On a more real-world case like linux.git, the speed difference isn't
measurable at all, simply because most trees aren't that deep and
there's so much other work going on (like accessing the objects
themselves). So the improvement I saw should be taken as evidence that
we're not making anything worse, but isn't really that interesting on
its own. The main motivation here is that we're now less likely to run
out of stack space and crash.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-31 06:17:54 +00:00
|
|
|
struct name_entry *entry;
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
int i;
|
tree-walk: reduce stack size for recursive functions
The traverse_trees() and traverse_trees_recursive() functions call each
other recursively. In a deep tree, this can result in running out of
stack space and crashing.
There's obviously going to be some limit here based on available stack,
but the problem is exacerbated by a few large structs, many of which we
over-allocate. For example, in traverse_trees() we store a name_entry
and tree_desc_x per tree, both of which contain an object_id (which is
now 32 bytes). And we allocate 8 of them (from MAX_TRAVERSE_TREES), even
though many traversals will only look at 1 or 2.
Interestingly, we used to allocate these on the heap, prior to
8dd40c0472 (traverse_trees(): use stack array for name entries,
2020-01-30). That commit was trying to simplify away allocation size
computations, and naively assumed that the sizes were small enough not
to matter. And they don't in normal cases, but on my stock Debian system
I see a crash running "git archive" on a tree with ~3600 entries.
That's deep enough we wouldn't see it in practice, but probably shallow
enough that we'd prefer not to make it a hard limit. Especially because
other systems may have even smaller stacks.
We can replace these stack variables with a few malloc invocations. This
reduces the stack sizes for the two functions from 1128 and 752 bytes,
respectively, down to 40 and 92 bytes. That allows a depth of ~13000 on
my machine (the improvement isn't in linear proportion because my
numbers don't count the size of parameters and other function overhead).
The possible downsides are:
1. We now have to remember to free(). But both functions have an easy
single exit (and already had to clean up other bits anyway).
2. The extra malloc()/free() overhead might be measurable. I tested
this by setting up a 3000-depth tree with a single blob and running
"git archive" on it. After switching to the heap, it consistently
runs 2-3% faster! Presumably this is because the 1K+ of wasted
stack space penalized memory caches.
On a more real-world case like linux.git, the speed difference isn't
measurable at all, simply because most trees aren't that deep and
there's so much other work going on (like accessing the objects
themselves). So the improvement I saw should be taken as evidence that
we're not making anything worse, but isn't really that interesting on
its own. The main motivation here is that we're now less likely to run
out of stack space and crash.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-31 06:17:54 +00:00
|
|
|
struct tree_desc_x *tx;
|
2011-08-29 19:26:05 +00:00
|
|
|
struct strbuf base = STRBUF_INIT;
|
|
|
|
int interesting = 1;
|
2015-12-21 22:34:20 +00:00
|
|
|
char *traverse_path;
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
|
2023-08-31 06:21:40 +00:00
|
|
|
if (traverse_trees_cur_depth > max_allowed_tree_depth)
|
|
|
|
return error("exceeded maximum allowed tree depth");
|
|
|
|
|
2021-01-04 03:09:10 +00:00
|
|
|
traverse_trees_count++;
|
|
|
|
traverse_trees_cur_depth++;
|
|
|
|
|
|
|
|
if (traverse_trees_cur_depth > traverse_trees_max_depth)
|
|
|
|
traverse_trees_max_depth = traverse_trees_cur_depth;
|
|
|
|
|
tree-walk: reduce stack size for recursive functions
The traverse_trees() and traverse_trees_recursive() functions call each
other recursively. In a deep tree, this can result in running out of
stack space and crashing.
There's obviously going to be some limit here based on available stack,
but the problem is exacerbated by a few large structs, many of which we
over-allocate. For example, in traverse_trees() we store a name_entry
and tree_desc_x per tree, both of which contain an object_id (which is
now 32 bytes). And we allocate 8 of them (from MAX_TRAVERSE_TREES), even
though many traversals will only look at 1 or 2.
Interestingly, we used to allocate these on the heap, prior to
8dd40c0472 (traverse_trees(): use stack array for name entries,
2020-01-30). That commit was trying to simplify away allocation size
computations, and naively assumed that the sizes were small enough not
to matter. And they don't in normal cases, but on my stock Debian system
I see a crash running "git archive" on a tree with ~3600 entries.
That's deep enough we wouldn't see it in practice, but probably shallow
enough that we'd prefer not to make it a hard limit. Especially because
other systems may have even smaller stacks.
We can replace these stack variables with a few malloc invocations. This
reduces the stack sizes for the two functions from 1128 and 752 bytes,
respectively, down to 40 and 92 bytes. That allows a depth of ~13000 on
my machine (the improvement isn't in linear proportion because my
numbers don't count the size of parameters and other function overhead).
The possible downsides are:
1. We now have to remember to free(). But both functions have an easy
single exit (and already had to clean up other bits anyway).
2. The extra malloc()/free() overhead might be measurable. I tested
this by setting up a 3000-depth tree with a single blob and running
"git archive" on it. After switching to the heap, it consistently
runs 2-3% faster! Presumably this is because the 1K+ of wasted
stack space penalized memory caches.
On a more real-world case like linux.git, the speed difference isn't
measurable at all, simply because most trees aren't that deep and
there's so much other work going on (like accessing the objects
themselves). So the improvement I saw should be taken as evidence that
we're not making anything worse, but isn't really that interesting on
its own. The main motivation here is that we're now less likely to run
out of stack space and crash.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-31 06:17:54 +00:00
|
|
|
ALLOC_ARRAY(entry, n);
|
|
|
|
ALLOC_ARRAY(tx, n);
|
2020-01-30 09:53:38 +00:00
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
tx[i].d = t[i];
|
2020-01-30 09:53:38 +00:00
|
|
|
tx[i].skip = NULL;
|
|
|
|
}
|
2006-03-30 06:55:43 +00:00
|
|
|
|
2011-08-29 19:26:05 +00:00
|
|
|
if (info->prev) {
|
2019-07-31 04:38:23 +00:00
|
|
|
strbuf_make_traverse_path(&base, info->prev,
|
|
|
|
info->name, info->namelen);
|
|
|
|
strbuf_addch(&base, '/');
|
|
|
|
traverse_path = xstrndup(base.buf, base.len);
|
2015-12-21 22:34:20 +00:00
|
|
|
} else {
|
2019-07-31 04:38:15 +00:00
|
|
|
traverse_path = xstrndup(info->name, info->pathlen);
|
2011-08-29 19:26:05 +00:00
|
|
|
}
|
2015-12-21 22:34:20 +00:00
|
|
|
info->traverse_path = traverse_path;
|
2006-03-30 06:55:43 +00:00
|
|
|
for (;;) {
|
2013-07-19 20:26:32 +00:00
|
|
|
int trees_used;
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
unsigned long mask, dirmask;
|
|
|
|
const char *first = NULL;
|
|
|
|
int first_len = 0;
|
2011-09-11 19:39:32 +00:00
|
|
|
struct name_entry *e = NULL;
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
int len;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
e = entry + i;
|
|
|
|
extended_entry_extract(tx + i, e, NULL, 0);
|
|
|
|
}
|
2006-03-30 06:55:43 +00:00
|
|
|
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
/*
|
|
|
|
* A tree may have "t-2" at the current location even
|
|
|
|
* though it may have "t" that is a subtree behind it,
|
|
|
|
* and another tree may return "t". We want to grab
|
|
|
|
* all "t" from all trees to match in such a case.
|
|
|
|
*/
|
2006-03-30 06:55:43 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
e = entry + i;
|
|
|
|
if (!e->path)
|
2006-03-30 06:55:43 +00:00
|
|
|
continue;
|
2011-10-24 06:36:09 +00:00
|
|
|
len = tree_entry_len(e);
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
if (!first) {
|
|
|
|
first = e->path;
|
|
|
|
first_len = len;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (name_compare(e->path, len, first, first_len) < 0) {
|
|
|
|
first = e->path;
|
|
|
|
first_len = len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (first) {
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
e = entry + i;
|
|
|
|
extended_entry_extract(tx + i, e, first, first_len);
|
|
|
|
/* Cull the ones that are not the earliest */
|
|
|
|
if (!e->path)
|
2006-03-30 06:55:43 +00:00
|
|
|
continue;
|
2011-10-24 06:36:09 +00:00
|
|
|
len = tree_entry_len(e);
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
if (name_compare(e->path, len, first, first_len))
|
|
|
|
entry_clear(e);
|
2006-03-30 06:55:43 +00:00
|
|
|
}
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Now we have in entry[i] the earliest name from the trees */
|
|
|
|
mask = 0;
|
|
|
|
dirmask = 0;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
if (!entry[i].path)
|
|
|
|
continue;
|
2006-03-30 06:55:43 +00:00
|
|
|
mask |= 1ul << i;
|
2008-03-06 04:06:18 +00:00
|
|
|
if (S_ISDIR(entry[i].mode))
|
|
|
|
dirmask |= 1ul << i;
|
2011-08-29 19:26:05 +00:00
|
|
|
e = &entry[i];
|
2006-03-30 06:55:43 +00:00
|
|
|
}
|
|
|
|
if (!mask)
|
|
|
|
break;
|
2018-11-18 16:47:57 +00:00
|
|
|
interesting = prune_traversal(istate, e, info, &base, interesting);
|
2011-08-29 19:26:05 +00:00
|
|
|
if (interesting < 0)
|
|
|
|
break;
|
|
|
|
if (interesting) {
|
2013-07-19 20:26:32 +00:00
|
|
|
trees_used = info->fn(n, mask, dirmask, entry, info);
|
|
|
|
if (trees_used < 0) {
|
2023-08-31 06:19:22 +00:00
|
|
|
ret = trees_used;
|
2011-08-29 19:26:05 +00:00
|
|
|
if (!info->show_all_errors)
|
|
|
|
break;
|
|
|
|
}
|
2013-07-19 20:26:32 +00:00
|
|
|
mask &= trees_used;
|
2010-08-11 08:38:07 +00:00
|
|
|
}
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
for (i = 0; i < n; i++)
|
2008-03-06 03:44:06 +00:00
|
|
|
if (mask & (1ul << i))
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
update_extended_entry(tx + i, entry + i);
|
2006-03-30 06:55:43 +00:00
|
|
|
}
|
traverse_trees(): handle D/F conflict case sanely
traverse_trees() is supposed to call its callback with all the matching
entries from the given trees. The current algorithm keeps a pointer to
each of the tree being traversed, and feeds the entry with the earliest
name to the callback.
This breaks down if the trees being traversed looks like this:
A B
t-1 t
t-2 u
t/a v
When we are currently looking at an entry "t-1" in tree A, and tree B has
returned "t", feeding "t" from the B and not feeding anything from A, only
because "t-1" sorts later than "t", will miss an entry for a subtree "t"
behind the current entry in tree A.
This introduces extended_entry_extract() helper function that gives what
name is expected from the tree, and implements a mechanism to look-ahead
in the tree object using it, to make sure such a case is handled sanely.
Traversal in tree A in the above example will first return "t" to match
that of B, and then the next request for an entry to A then returns "t-1".
This roughly corresponds to what Linus's "prepare for one-entry lookahead"
wanted to do, but because this does implement look ahead, t6035 and one more
test in t1012 reveal that the approach would not work without adjusting the
side that walks the index in unpack_trees() as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-19 21:07:14 +00:00
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
free_extended_entry(tx + i);
|
tree-walk: reduce stack size for recursive functions
The traverse_trees() and traverse_trees_recursive() functions call each
other recursively. In a deep tree, this can result in running out of
stack space and crashing.
There's obviously going to be some limit here based on available stack,
but the problem is exacerbated by a few large structs, many of which we
over-allocate. For example, in traverse_trees() we store a name_entry
and tree_desc_x per tree, both of which contain an object_id (which is
now 32 bytes). And we allocate 8 of them (from MAX_TRAVERSE_TREES), even
though many traversals will only look at 1 or 2.
Interestingly, we used to allocate these on the heap, prior to
8dd40c0472 (traverse_trees(): use stack array for name entries,
2020-01-30). That commit was trying to simplify away allocation size
computations, and naively assumed that the sizes were small enough not
to matter. And they don't in normal cases, but on my stock Debian system
I see a crash running "git archive" on a tree with ~3600 entries.
That's deep enough we wouldn't see it in practice, but probably shallow
enough that we'd prefer not to make it a hard limit. Especially because
other systems may have even smaller stacks.
We can replace these stack variables with a few malloc invocations. This
reduces the stack sizes for the two functions from 1128 and 752 bytes,
respectively, down to 40 and 92 bytes. That allows a depth of ~13000 on
my machine (the improvement isn't in linear proportion because my
numbers don't count the size of parameters and other function overhead).
The possible downsides are:
1. We now have to remember to free(). But both functions have an easy
single exit (and already had to clean up other bits anyway).
2. The extra malloc()/free() overhead might be measurable. I tested
this by setting up a 3000-depth tree with a single blob and running
"git archive" on it. After switching to the heap, it consistently
runs 2-3% faster! Presumably this is because the 1K+ of wasted
stack space penalized memory caches.
On a more real-world case like linux.git, the speed difference isn't
measurable at all, simply because most trees aren't that deep and
there's so much other work going on (like accessing the objects
themselves). So the improvement I saw should be taken as evidence that
we're not making anything worse, but isn't really that interesting on
its own. The main motivation here is that we're now less likely to run
out of stack space and crash.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-31 06:17:54 +00:00
|
|
|
free(tx);
|
|
|
|
free(entry);
|
2015-12-21 22:34:20 +00:00
|
|
|
free(traverse_path);
|
|
|
|
info->traverse_path = NULL;
|
2011-08-29 19:26:05 +00:00
|
|
|
strbuf_release(&base);
|
2021-01-04 03:09:10 +00:00
|
|
|
|
|
|
|
traverse_trees_cur_depth--;
|
2023-08-31 06:19:22 +00:00
|
|
|
return ret;
|
2006-03-30 06:55:43 +00:00
|
|
|
}
|
|
|
|
|
2015-05-20 17:03:38 +00:00
|
|
|
struct dir_state {
|
|
|
|
void *tree;
|
|
|
|
unsigned long size;
|
2018-05-02 00:25:40 +00:00
|
|
|
struct object_id oid;
|
2015-05-20 17:03:38 +00:00
|
|
|
};
|
|
|
|
|
2019-06-27 09:28:49 +00:00
|
|
|
static int find_tree_entry(struct repository *r, struct tree_desc *t,
|
|
|
|
const char *name, struct object_id *result,
|
|
|
|
unsigned short *mode)
|
2006-04-19 21:05:47 +00:00
|
|
|
{
|
|
|
|
int namelen = strlen(name);
|
|
|
|
while (t->size) {
|
|
|
|
const char *entry;
|
2019-01-15 00:39:41 +00:00
|
|
|
struct object_id oid;
|
2006-04-19 21:05:47 +00:00
|
|
|
int entrylen, cmp;
|
|
|
|
|
2019-01-15 00:39:41 +00:00
|
|
|
oidcpy(&oid, tree_entry_extract(t, &entry, mode));
|
2011-10-24 06:36:09 +00:00
|
|
|
entrylen = tree_entry_len(&t->entry);
|
2006-04-19 21:05:47 +00:00
|
|
|
update_tree_entry(t);
|
|
|
|
if (entrylen > namelen)
|
|
|
|
continue;
|
|
|
|
cmp = memcmp(name, entry, entrylen);
|
|
|
|
if (cmp > 0)
|
|
|
|
continue;
|
|
|
|
if (cmp < 0)
|
|
|
|
break;
|
|
|
|
if (entrylen == namelen) {
|
2019-01-15 00:39:41 +00:00
|
|
|
oidcpy(result, &oid);
|
2006-04-19 21:05:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (name[entrylen] != '/')
|
|
|
|
continue;
|
|
|
|
if (!S_ISDIR(*mode))
|
|
|
|
break;
|
|
|
|
if (++entrylen == namelen) {
|
2019-01-15 00:39:41 +00:00
|
|
|
oidcpy(result, &oid);
|
2006-04-19 21:05:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2019-06-27 09:28:49 +00:00
|
|
|
return get_tree_entry(r, &oid, name + entrylen, result, mode);
|
2006-04-19 21:05:47 +00:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-06-27 09:28:49 +00:00
|
|
|
int get_tree_entry(struct repository *r,
|
|
|
|
const struct object_id *tree_oid,
|
|
|
|
const char *name,
|
|
|
|
struct object_id *oid,
|
|
|
|
unsigned short *mode)
|
2006-04-19 21:05:47 +00:00
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
void *tree;
|
2007-03-21 17:08:25 +00:00
|
|
|
unsigned long size;
|
2018-03-12 02:27:51 +00:00
|
|
|
struct object_id root;
|
2006-04-19 21:05:47 +00:00
|
|
|
|
2022-02-04 23:48:34 +00:00
|
|
|
tree = read_object_with_reference(r, tree_oid, OBJ_TREE, &size, &root);
|
2006-04-19 21:05:47 +00:00
|
|
|
if (!tree)
|
|
|
|
return -1;
|
2007-01-09 16:11:47 +00:00
|
|
|
|
|
|
|
if (name[0] == '\0') {
|
2018-03-12 02:27:51 +00:00
|
|
|
oidcpy(oid, &root);
|
2010-02-14 09:56:46 +00:00
|
|
|
free(tree);
|
2007-01-09 16:11:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-27 18:18:40 +00:00
|
|
|
if (!size) {
|
|
|
|
retval = -1;
|
|
|
|
} else {
|
|
|
|
struct tree_desc t;
|
|
|
|
init_tree_desc(&t, tree, size);
|
2019-06-27 09:28:49 +00:00
|
|
|
retval = find_tree_entry(r, &t, name, oid, mode);
|
2011-10-27 18:18:40 +00:00
|
|
|
}
|
2006-04-19 21:05:47 +00:00
|
|
|
free(tree);
|
|
|
|
return retval;
|
|
|
|
}
|
2010-12-15 15:02:40 +00:00
|
|
|
|
2015-05-20 17:03:38 +00:00
|
|
|
/*
|
|
|
|
* This is Linux's built-in max for the number of symlinks to follow.
|
|
|
|
* That limit, of course, does not affect git, but it's a reasonable
|
|
|
|
* choice.
|
|
|
|
*/
|
|
|
|
#define GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS 40
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find a tree entry by following symlinks in tree_sha (which is
|
|
|
|
* assumed to be the root of the repository). In the event that a
|
|
|
|
* symlink points outside the repository (e.g. a link to /foo or a
|
|
|
|
* root-level link to ../foo), the portion of the link which is
|
|
|
|
* outside the repository will be returned in result_path, and *mode
|
|
|
|
* will be set to 0. It is assumed that result_path is uninitialized.
|
|
|
|
* If there are no symlinks, or the end result of the symlink chain
|
|
|
|
* points to an object inside the repository, result will be filled in
|
|
|
|
* with the sha1 of the found object, and *mode will hold the mode of
|
|
|
|
* the object.
|
|
|
|
*
|
2019-01-18 04:19:43 +00:00
|
|
|
* See the code for enum get_oid_result for a description of
|
2015-05-20 17:03:38 +00:00
|
|
|
* the return values.
|
|
|
|
*/
|
2019-06-27 09:28:50 +00:00
|
|
|
enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
|
|
|
|
struct object_id *tree_oid, const char *name,
|
|
|
|
struct object_id *result, struct strbuf *result_path,
|
|
|
|
unsigned short *mode)
|
2015-05-20 17:03:38 +00:00
|
|
|
{
|
|
|
|
int retval = MISSING_OBJECT;
|
|
|
|
struct dir_state *parents = NULL;
|
|
|
|
size_t parents_alloc = 0;
|
2017-09-21 16:49:38 +00:00
|
|
|
size_t i, parents_nr = 0;
|
2018-03-12 02:27:49 +00:00
|
|
|
struct object_id current_tree_oid;
|
2015-05-20 17:03:38 +00:00
|
|
|
struct strbuf namebuf = STRBUF_INIT;
|
|
|
|
struct tree_desc t;
|
|
|
|
int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
|
|
|
|
|
|
|
|
init_tree_desc(&t, NULL, 0UL);
|
|
|
|
strbuf_addstr(&namebuf, name);
|
2018-05-02 00:25:40 +00:00
|
|
|
oidcpy(¤t_tree_oid, tree_oid);
|
2015-05-20 17:03:38 +00:00
|
|
|
|
|
|
|
while (1) {
|
|
|
|
int find_result;
|
|
|
|
char *first_slash;
|
|
|
|
char *remainder = NULL;
|
|
|
|
|
|
|
|
if (!t.buffer) {
|
|
|
|
void *tree;
|
2018-03-12 02:27:49 +00:00
|
|
|
struct object_id root;
|
2015-05-20 17:03:38 +00:00
|
|
|
unsigned long size;
|
2019-06-27 09:28:50 +00:00
|
|
|
tree = read_object_with_reference(r,
|
2019-06-27 09:28:47 +00:00
|
|
|
¤t_tree_oid,
|
2022-02-04 23:48:34 +00:00
|
|
|
OBJ_TREE, &size,
|
2018-03-12 02:27:52 +00:00
|
|
|
&root);
|
2015-05-20 17:03:38 +00:00
|
|
|
if (!tree)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
ALLOC_GROW(parents, parents_nr + 1, parents_alloc);
|
|
|
|
parents[parents_nr].tree = tree;
|
|
|
|
parents[parents_nr].size = size;
|
2018-05-02 00:25:40 +00:00
|
|
|
oidcpy(&parents[parents_nr].oid, &root);
|
2015-05-20 17:03:38 +00:00
|
|
|
parents_nr++;
|
|
|
|
|
|
|
|
if (namebuf.buf[0] == '\0') {
|
2018-05-02 00:25:40 +00:00
|
|
|
oidcpy(result, &root);
|
2015-05-20 17:03:38 +00:00
|
|
|
retval = FOUND;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!size)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* descend */
|
|
|
|
init_tree_desc(&t, tree, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle symlinks to e.g. a//b by removing leading slashes */
|
|
|
|
while (namebuf.buf[0] == '/') {
|
|
|
|
strbuf_remove(&namebuf, 0, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Split namebuf into a first component and a remainder */
|
|
|
|
if ((first_slash = strchr(namebuf.buf, '/'))) {
|
|
|
|
*first_slash = 0;
|
|
|
|
remainder = first_slash + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(namebuf.buf, "..")) {
|
|
|
|
struct dir_state *parent;
|
|
|
|
/*
|
|
|
|
* We could end up with .. in the namebuf if it
|
|
|
|
* appears in a symlink.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (parents_nr == 1) {
|
|
|
|
if (remainder)
|
|
|
|
*first_slash = '/';
|
|
|
|
strbuf_add(result_path, namebuf.buf,
|
|
|
|
namebuf.len);
|
|
|
|
*mode = 0;
|
|
|
|
retval = FOUND;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
parent = &parents[parents_nr - 1];
|
|
|
|
free(parent->tree);
|
|
|
|
parents_nr--;
|
|
|
|
parent = &parents[parents_nr - 1];
|
|
|
|
init_tree_desc(&t, parent->tree, parent->size);
|
|
|
|
strbuf_remove(&namebuf, 0, remainder ? 3 : 2);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We could end up here via a symlink to dir/.. */
|
|
|
|
if (namebuf.buf[0] == '\0') {
|
2018-05-02 00:25:40 +00:00
|
|
|
oidcpy(result, &parents[parents_nr - 1].oid);
|
2015-05-20 17:03:38 +00:00
|
|
|
retval = FOUND;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Look up the first (or only) path component in the tree. */
|
2019-06-27 09:28:50 +00:00
|
|
|
find_result = find_tree_entry(r, &t, namebuf.buf,
|
2018-03-12 02:27:51 +00:00
|
|
|
¤t_tree_oid, mode);
|
2015-05-20 17:03:38 +00:00
|
|
|
if (find_result) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (S_ISDIR(*mode)) {
|
|
|
|
if (!remainder) {
|
2018-05-02 00:25:40 +00:00
|
|
|
oidcpy(result, ¤t_tree_oid);
|
2015-05-20 17:03:38 +00:00
|
|
|
retval = FOUND;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
/* Descend the tree */
|
|
|
|
t.buffer = NULL;
|
|
|
|
strbuf_remove(&namebuf, 0,
|
|
|
|
1 + first_slash - namebuf.buf);
|
|
|
|
} else if (S_ISREG(*mode)) {
|
|
|
|
if (!remainder) {
|
2018-05-02 00:25:40 +00:00
|
|
|
oidcpy(result, ¤t_tree_oid);
|
2015-05-20 17:03:38 +00:00
|
|
|
retval = FOUND;
|
|
|
|
} else {
|
|
|
|
retval = NOT_DIR;
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
} else if (S_ISLNK(*mode)) {
|
|
|
|
/* Follow a symlink */
|
|
|
|
unsigned long link_len;
|
|
|
|
size_t len;
|
|
|
|
char *contents, *contents_start;
|
|
|
|
struct dir_state *parent;
|
|
|
|
enum object_type type;
|
|
|
|
|
|
|
|
if (follows_remaining-- == 0) {
|
|
|
|
/* Too many symlinks followed */
|
|
|
|
retval = SYMLINK_LOOP;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point, we have followed at a least
|
|
|
|
* one symlink, so on error we need to report this.
|
|
|
|
*/
|
|
|
|
retval = DANGLING_SYMLINK;
|
|
|
|
|
2019-06-27 09:28:50 +00:00
|
|
|
contents = repo_read_object_file(r,
|
|
|
|
¤t_tree_oid, &type,
|
sha1_file: convert read_sha1_file to struct object_id
Convert read_sha1_file to take a pointer to struct object_id and rename
it read_object_file. Do the same for read_sha1_file_extended.
Convert one use in grep.c to use the new function without any other code
change, since the pointer being passed is a void pointer that is already
initialized with a pointer to struct object_id. Update the declaration
and definitions of the modified functions, and apply the following
semantic patch to convert the remaining callers:
@@
expression E1, E2, E3;
@@
- read_sha1_file(E1.hash, E2, E3)
+ read_object_file(&E1, E2, E3)
@@
expression E1, E2, E3;
@@
- read_sha1_file(E1->hash, E2, E3)
+ read_object_file(E1, E2, E3)
@@
expression E1, E2, E3, E4;
@@
- read_sha1_file_extended(E1.hash, E2, E3, E4)
+ read_object_file_extended(&E1, E2, E3, E4)
@@
expression E1, E2, E3, E4;
@@
- read_sha1_file_extended(E1->hash, E2, E3, E4)
+ read_object_file_extended(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-12 02:27:53 +00:00
|
|
|
&link_len);
|
2015-05-20 17:03:38 +00:00
|
|
|
|
|
|
|
if (!contents)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
if (contents[0] == '/') {
|
|
|
|
strbuf_addstr(result_path, contents);
|
|
|
|
free(contents);
|
|
|
|
*mode = 0;
|
|
|
|
retval = FOUND;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (remainder)
|
|
|
|
len = first_slash - namebuf.buf;
|
|
|
|
else
|
|
|
|
len = namebuf.len;
|
|
|
|
|
|
|
|
contents_start = contents;
|
|
|
|
|
|
|
|
parent = &parents[parents_nr - 1];
|
|
|
|
init_tree_desc(&t, parent->tree, parent->size);
|
|
|
|
strbuf_splice(&namebuf, 0, len,
|
|
|
|
contents_start, link_len);
|
|
|
|
if (remainder)
|
|
|
|
namebuf.buf[link_len] = '/';
|
|
|
|
free(contents);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
for (i = 0; i < parents_nr; i++)
|
|
|
|
free(parents[i].tree);
|
|
|
|
free(parents);
|
|
|
|
|
|
|
|
strbuf_release(&namebuf);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2013-07-14 08:36:09 +00:00
|
|
|
static int match_entry(const struct pathspec_item *item,
|
|
|
|
const struct name_entry *entry, int pathlen,
|
2010-12-15 15:02:43 +00:00
|
|
|
const char *match, int matchlen,
|
2012-10-19 17:14:42 +00:00
|
|
|
enum interesting *never_interesting)
|
2010-12-15 15:02:43 +00:00
|
|
|
{
|
|
|
|
int m = -1; /* signals that we haven't called strncmp() */
|
|
|
|
|
2013-07-14 08:36:09 +00:00
|
|
|
if (item->magic & PATHSPEC_ICASE)
|
|
|
|
/*
|
|
|
|
* "Never interesting" trick requires exact
|
|
|
|
* matching. We could do something clever with inexact
|
|
|
|
* matching, but it's trickier (and not to forget that
|
|
|
|
* strcasecmp is locale-dependent, at least in
|
|
|
|
* glibc). Just disable it for now. It can't be worse
|
|
|
|
* than the wildcard's codepath of '[Tt][Hi][Is][Ss]'
|
|
|
|
* pattern.
|
|
|
|
*/
|
|
|
|
*never_interesting = entry_not_interesting;
|
|
|
|
else if (*never_interesting != entry_not_interesting) {
|
2010-12-15 15:02:43 +00:00
|
|
|
/*
|
|
|
|
* We have not seen any match that sorts later
|
|
|
|
* than the current path.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Does match sort strictly earlier than path
|
|
|
|
* with their common parts?
|
|
|
|
*/
|
|
|
|
m = strncmp(match, entry->path,
|
|
|
|
(matchlen < pathlen) ? matchlen : pathlen);
|
|
|
|
if (m < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we come here even once, that means there is at
|
|
|
|
* least one pathspec that would sort equal to or
|
|
|
|
* later than the path we are currently looking at.
|
|
|
|
* In other words, if we have never reached this point
|
|
|
|
* after iterating all pathspecs, it means all
|
|
|
|
* pathspecs are either outside of base, or inside the
|
|
|
|
* base but sorts strictly earlier than the current
|
|
|
|
* one. In either case, they will never match the
|
|
|
|
* subsequent entries. In such a case, we initialized
|
|
|
|
* the variable to -1 and that is what will be
|
|
|
|
* returned, allowing the caller to terminate early.
|
|
|
|
*/
|
2012-10-19 17:14:42 +00:00
|
|
|
*never_interesting = entry_not_interesting;
|
2010-12-15 15:02:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pathlen > matchlen)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (matchlen > pathlen) {
|
|
|
|
if (match[pathlen] != '/')
|
|
|
|
return 0;
|
2020-06-05 13:00:23 +00:00
|
|
|
/*
|
|
|
|
* Reject non-directories as partial pathnames, except
|
|
|
|
* when match is a submodule with a trailing slash and
|
|
|
|
* nothing else (to handle 'submod/' and 'submod'
|
|
|
|
* uniformly).
|
|
|
|
*/
|
|
|
|
if (!S_ISDIR(entry->mode) &&
|
|
|
|
(!S_ISGITLINK(entry->mode) || matchlen > pathlen + 1))
|
2010-12-15 15:02:43 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m == -1)
|
|
|
|
/*
|
|
|
|
* we cheated and did not do strncmp(), so we do
|
|
|
|
* that here.
|
|
|
|
*/
|
2013-07-14 08:36:09 +00:00
|
|
|
m = ps_strncmp(item, match, entry->path, pathlen);
|
2010-12-15 15:02:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If common part matched earlier then it is a hit,
|
|
|
|
* because we rejected the case where path is not a
|
|
|
|
* leading directory and is shorter than match.
|
|
|
|
*/
|
|
|
|
if (!m)
|
2013-07-14 08:36:09 +00:00
|
|
|
/*
|
|
|
|
* match_entry does not check if the prefix part is
|
|
|
|
* matched case-sensitively. If the entry is a
|
|
|
|
* directory and part of prefix, it'll be rematched
|
|
|
|
* eventually by basecmp with special treatment for
|
|
|
|
* the prefix.
|
|
|
|
*/
|
2010-12-15 15:02:43 +00:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-14 08:36:09 +00:00
|
|
|
/* :(icase)-aware string compare */
|
|
|
|
static int basecmp(const struct pathspec_item *item,
|
|
|
|
const char *base, const char *match, int len)
|
|
|
|
{
|
|
|
|
if (item->magic & PATHSPEC_ICASE) {
|
|
|
|
int ret, n = len > item->prefix ? item->prefix : len;
|
|
|
|
ret = strncmp(base, match, n);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
base += n;
|
|
|
|
match += n;
|
|
|
|
len -= n;
|
|
|
|
}
|
|
|
|
return ps_strncmp(item, base, match, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int match_dir_prefix(const struct pathspec_item *item,
|
|
|
|
const char *base,
|
2010-12-15 15:02:43 +00:00
|
|
|
const char *match, int matchlen)
|
|
|
|
{
|
2013-07-14 08:36:09 +00:00
|
|
|
if (basecmp(item, base, match, matchlen))
|
2010-12-15 15:02:43 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the base is a subdirectory of a path which
|
|
|
|
* was specified, all of them are interesting.
|
|
|
|
*/
|
|
|
|
if (!matchlen ||
|
|
|
|
base[matchlen] == '/' ||
|
|
|
|
match[matchlen - 1] == '/')
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Just a random prefix match */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-24 04:33:51 +00:00
|
|
|
/*
|
|
|
|
* Perform matching on the leading non-wildcard part of
|
|
|
|
* pathspec. item->nowildcard_len must be greater than zero. Return
|
|
|
|
* non-zero if base is matched.
|
|
|
|
*/
|
|
|
|
static int match_wildcard_base(const struct pathspec_item *item,
|
|
|
|
const char *base, int baselen,
|
|
|
|
int *matched)
|
|
|
|
{
|
|
|
|
const char *match = item->match;
|
|
|
|
/* the wildcard part is not considered in this function */
|
|
|
|
int matchlen = item->nowildcard_len;
|
|
|
|
|
|
|
|
if (baselen) {
|
|
|
|
int dirlen;
|
|
|
|
/*
|
|
|
|
* Return early if base is longer than the
|
|
|
|
* non-wildcard part but it does not match.
|
|
|
|
*/
|
|
|
|
if (baselen >= matchlen) {
|
|
|
|
*matched = matchlen;
|
2013-07-14 08:36:09 +00:00
|
|
|
return !basecmp(item, base, match, matchlen);
|
2012-11-24 04:33:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dirlen = matchlen;
|
|
|
|
while (dirlen && match[dirlen - 1] != '/')
|
|
|
|
dirlen--;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return early if base is shorter than the
|
|
|
|
* non-wildcard part but it does not match. Note that
|
|
|
|
* base ends with '/' so we are sure it really matches
|
|
|
|
* directory
|
|
|
|
*/
|
2013-07-14 08:36:09 +00:00
|
|
|
if (basecmp(item, base, match, baselen))
|
2012-11-24 04:33:51 +00:00
|
|
|
return 0;
|
|
|
|
*matched = baselen;
|
|
|
|
} else
|
|
|
|
*matched = 0;
|
|
|
|
/*
|
|
|
|
* we could have checked entry against the non-wildcard part
|
|
|
|
* that is not in base and does similar never_interesting
|
|
|
|
* optimization as in match_entry. For now just be happy with
|
|
|
|
* base comparison.
|
|
|
|
*/
|
|
|
|
return entry_interesting;
|
|
|
|
}
|
|
|
|
|
2010-12-15 15:02:40 +00:00
|
|
|
/*
|
|
|
|
* Is a tree entry interesting given the pathspec we have?
|
|
|
|
*
|
2023-07-07 22:21:16 +00:00
|
|
|
* Pre-condition: either baselen == 0 (i.e. empty path)
|
2010-12-17 12:45:33 +00:00
|
|
|
* or base[baselen-1] == '/' (i.e. with trailing slash).
|
2010-12-15 15:02:40 +00:00
|
|
|
*/
|
2018-11-18 16:47:57 +00:00
|
|
|
static enum interesting do_match(struct index_state *istate,
|
|
|
|
const struct name_entry *entry,
|
2023-07-07 22:21:16 +00:00
|
|
|
struct strbuf *base,
|
2013-12-06 07:30:48 +00:00
|
|
|
const struct pathspec *ps,
|
|
|
|
int exclude)
|
2010-12-15 15:02:40 +00:00
|
|
|
{
|
|
|
|
int i;
|
2023-07-07 22:21:16 +00:00
|
|
|
int pathlen, baselen = base->len;
|
2012-10-19 17:14:42 +00:00
|
|
|
enum interesting never_interesting = ps->has_wildcard ?
|
2011-10-24 06:36:10 +00:00
|
|
|
entry_not_interesting : all_entries_not_interesting;
|
2010-12-15 15:02:40 +00:00
|
|
|
|
2013-07-14 08:36:06 +00:00
|
|
|
GUARD_PATHSPEC(ps,
|
|
|
|
PATHSPEC_FROMTOP |
|
|
|
|
PATHSPEC_MAXDEPTH |
|
2013-07-14 08:36:08 +00:00
|
|
|
PATHSPEC_LITERAL |
|
2013-07-14 08:36:09 +00:00
|
|
|
PATHSPEC_GLOB |
|
2013-12-06 07:30:48 +00:00
|
|
|
PATHSPEC_ICASE |
|
2018-11-18 16:48:00 +00:00
|
|
|
PATHSPEC_EXCLUDE |
|
|
|
|
PATHSPEC_ATTR);
|
2013-07-14 08:35:36 +00:00
|
|
|
|
2010-12-15 15:02:44 +00:00
|
|
|
if (!ps->nr) {
|
2013-07-14 08:35:32 +00:00
|
|
|
if (!ps->recursive ||
|
|
|
|
!(ps->magic & PATHSPEC_MAXDEPTH) ||
|
|
|
|
ps->max_depth == -1)
|
2011-10-24 06:36:10 +00:00
|
|
|
return all_entries_interesting;
|
2023-07-07 22:21:16 +00:00
|
|
|
return within_depth(base->buf, baselen,
|
2011-10-24 06:36:10 +00:00
|
|
|
!!S_ISDIR(entry->mode),
|
|
|
|
ps->max_depth) ?
|
|
|
|
entry_interesting : entry_not_interesting;
|
2010-12-15 15:02:44 +00:00
|
|
|
}
|
2010-12-15 15:02:40 +00:00
|
|
|
|
2011-10-24 06:36:09 +00:00
|
|
|
pathlen = tree_entry_len(entry);
|
2010-12-15 15:02:40 +00:00
|
|
|
|
2010-12-17 12:45:33 +00:00
|
|
|
for (i = ps->nr - 1; i >= 0; i--) {
|
2010-12-15 15:02:40 +00:00
|
|
|
const struct pathspec_item *item = ps->items+i;
|
|
|
|
const char *match = item->match;
|
2023-07-07 22:21:16 +00:00
|
|
|
const char *base_str = base->buf;
|
2012-11-24 04:33:51 +00:00
|
|
|
int matchlen = item->len, matched = 0;
|
2010-12-15 15:02:40 +00:00
|
|
|
|
2013-12-06 07:30:48 +00:00
|
|
|
if ((!exclude && item->magic & PATHSPEC_EXCLUDE) ||
|
|
|
|
( exclude && !(item->magic & PATHSPEC_EXCLUDE)))
|
|
|
|
continue;
|
|
|
|
|
2010-12-15 15:02:40 +00:00
|
|
|
if (baselen >= matchlen) {
|
|
|
|
/* If it doesn't match, move along... */
|
2013-07-14 08:36:09 +00:00
|
|
|
if (!match_dir_prefix(item, base_str, match, matchlen))
|
2010-12-15 15:02:46 +00:00
|
|
|
goto match_wildcards;
|
2010-12-15 15:02:44 +00:00
|
|
|
|
2013-07-14 08:35:32 +00:00
|
|
|
if (!ps->recursive ||
|
|
|
|
!(ps->magic & PATHSPEC_MAXDEPTH) ||
|
2018-11-18 16:48:00 +00:00
|
|
|
ps->max_depth == -1) {
|
|
|
|
if (!item->attr_match_nr)
|
|
|
|
return all_entries_interesting;
|
|
|
|
else
|
|
|
|
goto interesting;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (within_depth(base_str + matchlen + 1,
|
|
|
|
baselen - matchlen - 1,
|
|
|
|
!!S_ISDIR(entry->mode),
|
|
|
|
ps->max_depth))
|
|
|
|
goto interesting;
|
|
|
|
else
|
|
|
|
return entry_not_interesting;
|
2010-12-15 15:02:40 +00:00
|
|
|
}
|
|
|
|
|
tree-walk: micro-optimization in tree_entry_interesting
In the case of a wide breadth top-level tree (~2400 entries, all trees
in this case), we can see a noticeable cost in the profiler calling
strncmp() here. Most of the time we are at the base level of the
repository, so base is "" and baselen == 0, which means we will always
test true. Break out this one tiny case so we can short circuit the
strncmp() call.
Test cases are as follows. packages.git is the Arch Linux git-svn clone
of the packages repository which has the characteristics above.
Commands:
[1] packages.git, /usr/bin/time git log >/dev/null
[2] packages.git, /usr/bin/time git log -- autogen/trunk pacman/trunk wget/trunk >/dev/null
[3] linux.git, /usr/bin/time git log >/dev/null
[4] linux.git, /usr/bin/time git log -- drivers/ata drivers/uio tools >/dev/null
Results:
before after %faster
[1] 2.56 2.55 0.4%
[2] 51.82 48.66 6.5%
[3] 5.58 5.61 -0.5%
[4] 1.55 1.51 0.2%
The takeaway here is this doesn't matter in many operations, but it does
for a certain style of repository and operation where it nets a 6.5%
measured improvement. The other changes are likely not significant by
reasonable statistics methods.
Note: the measured improvement when originally submitted was ~11% (43 to
38 secs) for operation [2]. At the time, the repository had 117220
commits; it now has 137537 commits.
Signed-off-by: Dan McGee <dpmcgee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-09-09 02:02:46 +00:00
|
|
|
/* Either there must be no base, or the base must match. */
|
2013-07-14 08:36:09 +00:00
|
|
|
if (baselen == 0 || !basecmp(item, base_str, match, baselen)) {
|
|
|
|
if (match_entry(item, entry, pathlen,
|
2010-12-15 15:02:43 +00:00
|
|
|
match + baselen, matchlen - baselen,
|
|
|
|
&never_interesting))
|
2018-11-18 16:48:00 +00:00
|
|
|
goto interesting;
|
2010-12-15 15:02:47 +00:00
|
|
|
|
2012-11-18 09:13:06 +00:00
|
|
|
if (item->nowildcard_len < item->len) {
|
2013-07-14 08:36:08 +00:00
|
|
|
if (!git_fnmatch(item, match + baselen, entry->path,
|
2012-11-24 04:33:50 +00:00
|
|
|
item->nowildcard_len - baselen))
|
2018-11-18 16:48:00 +00:00
|
|
|
goto interesting;
|
2010-12-15 15:02:47 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Match all directories. We'll try to
|
|
|
|
* match files later on.
|
|
|
|
*/
|
|
|
|
if (ps->recursive && S_ISDIR(entry->mode))
|
2011-10-24 06:36:10 +00:00
|
|
|
return entry_interesting;
|
2016-12-16 19:03:21 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* When matching against submodules with
|
|
|
|
* wildcard characters, ensure that the entry
|
|
|
|
* at least matches up to the first wild
|
|
|
|
* character. More accurate matching can then
|
|
|
|
* be performed in the submodule itself.
|
|
|
|
*/
|
2017-12-05 00:07:34 +00:00
|
|
|
if (ps->recurse_submodules &&
|
|
|
|
S_ISGITLINK(entry->mode) &&
|
2016-12-16 19:03:21 +00:00
|
|
|
!ps_strncmp(item, match + baselen,
|
|
|
|
entry->path,
|
|
|
|
item->nowildcard_len - baselen))
|
2018-11-18 16:48:00 +00:00
|
|
|
goto interesting;
|
2010-12-15 15:02:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
2010-12-15 15:02:40 +00:00
|
|
|
}
|
2010-12-15 15:02:46 +00:00
|
|
|
|
|
|
|
match_wildcards:
|
2012-11-18 09:13:06 +00:00
|
|
|
if (item->nowildcard_len == item->len)
|
2010-12-15 15:02:46 +00:00
|
|
|
continue;
|
|
|
|
|
2012-11-24 04:33:51 +00:00
|
|
|
if (item->nowildcard_len &&
|
|
|
|
!match_wildcard_base(item, base_str, baselen, &matched))
|
2014-01-25 22:06:46 +00:00
|
|
|
continue;
|
2012-11-24 04:33:51 +00:00
|
|
|
|
2010-12-15 15:02:46 +00:00
|
|
|
/*
|
|
|
|
* Concatenate base and entry->path into one and do
|
|
|
|
* fnmatch() on it.
|
2012-11-24 04:33:51 +00:00
|
|
|
*
|
|
|
|
* While we could avoid concatenation in certain cases
|
|
|
|
* [1], which saves a memcpy and potentially a
|
|
|
|
* realloc, it turns out not worth it. Measurement on
|
|
|
|
* linux-2.6 does not show any clear improvements,
|
|
|
|
* partly because of the nowildcard_len optimization
|
|
|
|
* in git_fnmatch(). Avoid micro-optimizations here.
|
|
|
|
*
|
|
|
|
* [1] if match_wildcard_base() says the base
|
|
|
|
* directory is already matched, we only need to match
|
|
|
|
* the rest, which is shorter so _in theory_ faster.
|
2010-12-15 15:02:46 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
strbuf_add(base, entry->path, pathlen);
|
|
|
|
|
2023-07-07 22:21:16 +00:00
|
|
|
if (!git_fnmatch(item, match, base->buf,
|
2012-11-24 04:33:50 +00:00
|
|
|
item->nowildcard_len)) {
|
2023-07-07 22:21:16 +00:00
|
|
|
strbuf_setlen(base, baselen);
|
2018-11-18 16:48:00 +00:00
|
|
|
goto interesting;
|
2010-12-15 15:02:46 +00:00
|
|
|
}
|
2016-12-16 19:03:21 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* When matching against submodules with
|
|
|
|
* wildcard characters, ensure that the entry
|
|
|
|
* at least matches up to the first wild
|
|
|
|
* character. More accurate matching can then
|
|
|
|
* be performed in the submodule itself.
|
|
|
|
*/
|
2017-12-05 00:07:34 +00:00
|
|
|
if (ps->recurse_submodules && S_ISGITLINK(entry->mode) &&
|
2023-07-07 22:21:16 +00:00
|
|
|
!ps_strncmp(item, match, base->buf,
|
2016-12-16 19:03:21 +00:00
|
|
|
item->nowildcard_len)) {
|
2023-07-07 22:21:16 +00:00
|
|
|
strbuf_setlen(base, baselen);
|
2018-11-18 16:48:00 +00:00
|
|
|
goto interesting;
|
2016-12-16 19:03:21 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 22:21:16 +00:00
|
|
|
strbuf_setlen(base, baselen);
|
2010-12-15 15:02:46 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Match all directories. We'll try to match files
|
|
|
|
* later on.
|
2012-01-14 09:23:22 +00:00
|
|
|
* max_depth is ignored but we may consider support it
|
|
|
|
* in future, see
|
2019-11-27 12:54:04 +00:00
|
|
|
* https://lore.kernel.org/git/7vmxo5l2g4.fsf@alter.siamese.dyndns.org/
|
2010-12-15 15:02:46 +00:00
|
|
|
*/
|
|
|
|
if (ps->recursive && S_ISDIR(entry->mode))
|
2011-10-24 06:36:10 +00:00
|
|
|
return entry_interesting;
|
2018-11-18 16:48:00 +00:00
|
|
|
continue;
|
|
|
|
interesting:
|
|
|
|
if (item->attr_match_nr) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Must not return all_entries_not_interesting
|
|
|
|
* prematurely. We do not know if all entries do not
|
|
|
|
* match some attributes with current attr API.
|
|
|
|
*/
|
|
|
|
never_interesting = entry_not_interesting;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Consider all directories interesting (because some
|
|
|
|
* of those files inside may match some attributes
|
|
|
|
* even though the parent dir does not)
|
|
|
|
*
|
|
|
|
* FIXME: attributes _can_ match directories and we
|
|
|
|
* can probably return all_entries_interesting or
|
|
|
|
* all_entries_not_interesting here if matched.
|
|
|
|
*/
|
|
|
|
if (S_ISDIR(entry->mode))
|
|
|
|
return entry_interesting;
|
|
|
|
|
|
|
|
strbuf_add(base, entry->path, pathlen);
|
2023-07-07 22:21:16 +00:00
|
|
|
ret = match_pathspec_attrs(istate, base->buf,
|
|
|
|
base->len, item);
|
|
|
|
strbuf_setlen(base, baselen);
|
2018-11-18 16:48:00 +00:00
|
|
|
if (!ret)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
return entry_interesting;
|
2010-12-15 15:02:40 +00:00
|
|
|
}
|
|
|
|
return never_interesting; /* No matches */
|
|
|
|
}
|
2013-12-06 07:30:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Is a tree entry interesting given the pathspec we have?
|
|
|
|
*
|
2023-07-07 22:21:16 +00:00
|
|
|
* Pre-condition: either baselen == 0 (i.e. empty path)
|
2013-12-06 07:30:48 +00:00
|
|
|
* or base[baselen-1] == '/' (i.e. with trailing slash).
|
|
|
|
*/
|
2018-11-18 16:47:57 +00:00
|
|
|
enum interesting tree_entry_interesting(struct index_state *istate,
|
|
|
|
const struct name_entry *entry,
|
2023-07-07 22:21:15 +00:00
|
|
|
struct strbuf *base,
|
2013-12-06 07:30:48 +00:00
|
|
|
const struct pathspec *ps)
|
|
|
|
{
|
|
|
|
enum interesting positive, negative;
|
2023-07-07 22:21:16 +00:00
|
|
|
positive = do_match(istate, entry, base, ps, 0);
|
2013-12-06 07:30:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* case | entry | positive | negative | result
|
|
|
|
* -----+-------+----------+----------+-------
|
|
|
|
* 1 | file | -1 | -1..2 | -1
|
|
|
|
* 2 | file | 0 | -1..2 | 0
|
|
|
|
* 3 | file | 1 | -1 | 1
|
|
|
|
* 4 | file | 1 | 0 | 1
|
|
|
|
* 5 | file | 1 | 1 | 0
|
|
|
|
* 6 | file | 1 | 2 | 0
|
|
|
|
* 7 | file | 2 | -1 | 2
|
tree-walk.c: fix overoptimistic inclusion in :(exclude) matching
tree_entry_interesting() is used for matching pathspec on a tree. The
interesting thing about this function is that, because the tree
entries are known to be sorted, this function can return more than
just "yes, matched" and "no, not matched". It can also say "yes, this
entry is matched and so is the remaining entries in the tree".
This is where I made a mistake when matching exclude pathspec. For
exclude pathspec, we do matching twice, one with positive patterns and
one with negative ones, then a rule table is applied to determine the
final "include or exclude" result. Note that "matched" does not
necessarily mean include. For negative patterns, "matched" means
exclude.
This particular rule is too eager to include everything. Rule 8 says
that "if all entries are positively matched" and the current entry is
not negatively matched (i.e. not excluded), then all entries are
positively matched and therefore included. But this is not true. If
the _current_ entry is not negatively matched, it does not mean the
next one will not be and we cannot conclude right away that all
remaining entries are positively matched and can be included.
Rules 8 and 18 are now updated to be less eager. We conclude that the
current entry is positively matched and included. But we say nothing
about remaining entries. tree_entry_interesting() will be called again
for those entries where we will determine entries individually.
Reported-by: Christophe Bliard <christophe.bliard@trux.info>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-04 05:28:51 +00:00
|
|
|
* 8 | file | 2 | 0 | 1
|
2013-12-06 07:30:48 +00:00
|
|
|
* 9 | file | 2 | 1 | 0
|
|
|
|
* 10 | file | 2 | 2 | -1
|
|
|
|
* -----+-------+----------+----------+-------
|
|
|
|
* 11 | dir | -1 | -1..2 | -1
|
|
|
|
* 12 | dir | 0 | -1..2 | 0
|
|
|
|
* 13 | dir | 1 | -1 | 1
|
|
|
|
* 14 | dir | 1 | 0 | 1
|
|
|
|
* 15 | dir | 1 | 1 | 1 (*)
|
|
|
|
* 16 | dir | 1 | 2 | 0
|
|
|
|
* 17 | dir | 2 | -1 | 2
|
tree-walk.c: fix overoptimistic inclusion in :(exclude) matching
tree_entry_interesting() is used for matching pathspec on a tree. The
interesting thing about this function is that, because the tree
entries are known to be sorted, this function can return more than
just "yes, matched" and "no, not matched". It can also say "yes, this
entry is matched and so is the remaining entries in the tree".
This is where I made a mistake when matching exclude pathspec. For
exclude pathspec, we do matching twice, one with positive patterns and
one with negative ones, then a rule table is applied to determine the
final "include or exclude" result. Note that "matched" does not
necessarily mean include. For negative patterns, "matched" means
exclude.
This particular rule is too eager to include everything. Rule 8 says
that "if all entries are positively matched" and the current entry is
not negatively matched (i.e. not excluded), then all entries are
positively matched and therefore included. But this is not true. If
the _current_ entry is not negatively matched, it does not mean the
next one will not be and we cannot conclude right away that all
remaining entries are positively matched and can be included.
Rules 8 and 18 are now updated to be less eager. We conclude that the
current entry is positively matched and included. But we say nothing
about remaining entries. tree_entry_interesting() will be called again
for those entries where we will determine entries individually.
Reported-by: Christophe Bliard <christophe.bliard@trux.info>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-04 05:28:51 +00:00
|
|
|
* 18 | dir | 2 | 0 | 1
|
2013-12-06 07:30:48 +00:00
|
|
|
* 19 | dir | 2 | 1 | 1 (*)
|
|
|
|
* 20 | dir | 2 | 2 | -1
|
|
|
|
*
|
|
|
|
* (*) An exclude pattern interested in a directory does not
|
|
|
|
* necessarily mean it will exclude all of the directory. In
|
|
|
|
* wildcard case, it can't decide until looking at individual
|
|
|
|
* files inside. So don't write such directories off yet.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!(ps->magic & PATHSPEC_EXCLUDE) ||
|
|
|
|
positive <= entry_not_interesting) /* #1, #2, #11, #12 */
|
|
|
|
return positive;
|
|
|
|
|
2023-07-07 22:21:16 +00:00
|
|
|
negative = do_match(istate, entry, base, ps, 1);
|
2013-12-06 07:30:48 +00:00
|
|
|
|
tree-walk.c: fix overoptimistic inclusion in :(exclude) matching
tree_entry_interesting() is used for matching pathspec on a tree. The
interesting thing about this function is that, because the tree
entries are known to be sorted, this function can return more than
just "yes, matched" and "no, not matched". It can also say "yes, this
entry is matched and so is the remaining entries in the tree".
This is where I made a mistake when matching exclude pathspec. For
exclude pathspec, we do matching twice, one with positive patterns and
one with negative ones, then a rule table is applied to determine the
final "include or exclude" result. Note that "matched" does not
necessarily mean include. For negative patterns, "matched" means
exclude.
This particular rule is too eager to include everything. Rule 8 says
that "if all entries are positively matched" and the current entry is
not negatively matched (i.e. not excluded), then all entries are
positively matched and therefore included. But this is not true. If
the _current_ entry is not negatively matched, it does not mean the
next one will not be and we cannot conclude right away that all
remaining entries are positively matched and can be included.
Rules 8 and 18 are now updated to be less eager. We conclude that the
current entry is positively matched and included. But we say nothing
about remaining entries. tree_entry_interesting() will be called again
for those entries where we will determine entries individually.
Reported-by: Christophe Bliard <christophe.bliard@trux.info>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-04 05:28:51 +00:00
|
|
|
/* #8, #18 */
|
|
|
|
if (positive == all_entries_interesting &&
|
|
|
|
negative == entry_not_interesting)
|
|
|
|
return entry_interesting;
|
|
|
|
|
|
|
|
/* #3, #4, #7, #13, #14, #17 */
|
2013-12-06 07:30:48 +00:00
|
|
|
if (negative <= entry_not_interesting)
|
|
|
|
return positive;
|
|
|
|
|
|
|
|
/* #15, #19 */
|
|
|
|
if (S_ISDIR(entry->mode) &&
|
|
|
|
positive >= entry_interesting &&
|
|
|
|
negative == entry_interesting)
|
|
|
|
return entry_interesting;
|
|
|
|
|
|
|
|
if ((positive == entry_interesting &&
|
|
|
|
negative >= entry_interesting) || /* #5, #6, #16 */
|
|
|
|
(positive == all_entries_interesting &&
|
|
|
|
negative == entry_interesting)) /* #9 */
|
|
|
|
return entry_not_interesting;
|
|
|
|
|
|
|
|
return all_entries_not_interesting; /* #10, #20 */
|
|
|
|
}
|