{"thread":{"id":"31414","subject":"[PATCH 3/9] Rename cryptic 'which' variable to more consistent name","startedAt":"2012-09-02T00:12:02Z","lastAt":"2012-12-26T15:46:17Z","messageCount":80,"participants":["Adam Spiers","Nguyen Thai Ngoc Duy","Philip Oakley","Junio C Hamano","Thiago Farina","Johannes Sixt","Michael Haggerty"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"198219","messageId":"1346544731-938-1-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":null,"subject":"[PATCH 0/9] new git check-ignore sub-command","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-02T00:12:02Z","receivedAt":"2012-09-02T00:12:02Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"I was browsing stackoverflow the other day and came across this question:\n\n    http://stackoverflow.com/questions/12144633/which-gitignore-rule-is-ignoring-my-file/\n\nA quick google revealed this thread from 2009:\n\n    http://thread.gmane.org/gmane.comp.version-control.git/108671/focus=108815\n\nwhere Junio and Jeff discussed the possibility of adding a new `git\ncheck-ignore' subcommand somewhat analogous to the existing `git\ncheck-attr', and suggested the beginnings of an implementation.  It\nstruck me that it might not be too hard to follow these ideas to their\nnatural conclusion, so I decided it would make a fun project :-)\n\nThe following series of patches is the outcome.  I am completely new\nto git hacking, so whilst I have tried very hard to follow all the\nconventions and documented guidelines, please go easy on me if there\nare any glaring errors ;-)  However, the added test suite should cover\nthe new code paths thoroughly, and I also ran check-ignore through\nvalgrind and made some improvements accordingly, so hopefully it's\npretty near the mark.\n\nI have a question and some comments about my current patch series.\n\nFirstly, I re-used pathspec-handling code from builtin/add.c, so I\nmoved it to a new pathspec.c file.  It looks like setup.c might have\nbeen a better candidate, but that library is already a fairly large\ncollection of apparently loosely associated things, so I wasn't sure.\nAccording to the comments, get_pathspec() is due to be superceded by\nthe \"struct pathspec\" interface, so perhaps it would make sense to\nsplit setup.c up into pathspec.c and one or two other files so as to\nmove towards a clean demarcation of this new API?\n\nSecondly, in the course of trying to understand the code base, my\nlittle brain got confused and I noticed a few areas where I thought\nthere was potential to make things a bit clearer.  So some of my\ncommits are janitorial in nature.\n\nThirdly, currently the new sub-command hardly looks at the cache.\nThis is partially because it doesn't need to in the most common use\ncase (i.e. user is confused about why files are/aren't being ignored).\nIt's also because this whole project took a lot longer than I\nexpected, so I'm running out of time :-) Perhaps someone can add this\nin the future if it's needed.  Right now the cache is only used to\nprevent recursing into submodules.\n\nThanks,\nAdam\n\nAdam Spiers (9):\n  Update directory listing API doc to match code\n  Improve documentation and comments regarding directory traversal API\n  Rename cryptic 'which' variable to more consistent name\n  Refactor excluded_from_list\n  Refactor excluded and path_excluded\n  For each exclude pattern, store information about where it came from\n  Extract some useful pathspec handling code from builtin/add.c into a\n    library\n  Provide free_directory() for reclaiming dir_struct memory\n  Add git-check-ignores\n\n .gitignore                                        |   1 +\n Documentation/git-check-ignore.txt                |  58 +++++\n Documentation/gitignore.txt                       |   6 +-\n Documentation/technical/api-directory-listing.txt |  23 +-\n Makefile                                          |   3 +\n builtin.h                                         |   1 +\n builtin/add.c                                     |  84 +-----\n builtin/check-ignore.c                            | 150 +++++++++++\n builtin/clean.c                                   |   2 +-\n builtin/ls-files.c                                |   3 +-\n command-list.txt                                  |   1 +\n contrib/completion/git-completion.bash            |   1 +\n dir.c                                             | 183 ++++++++++---\n dir.h                                             |  37 ++-\n git.c                                             |   1 +\n pathspec.c                                        |  87 +++++++\n pathspec.h                                        |   6 +\n t/t0007-ignores.sh                                | 301 ++++++++++++++++++++++\n 18 files changed, 811 insertions(+), 137 deletions(-)\n create mode 100644 Documentation/git-check-ignore.txt\n create mode 100644 builtin/check-ignore.c\n create mode 100644 pathspec.c\n create mode 100644 pathspec.h\n create mode 100755 t/t0007-ignores.sh\n\n-- \n1.7.12.155.ge5750d5.dirty\n"},{"id":"198217","messageId":"1346544731-938-2-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1346544731-938-1-git-send-email-git@adamspiers.org","subject":"[PATCH 1/9] Update directory listing API doc to match code","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-02T00:12:03Z","receivedAt":"2012-09-02T00:12:03Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"7c4c97c0ac turned the flags in struct dir_struct into a single bitfield\nvariable, but forgot to update this document.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n Documentation/technical/api-directory-listing.txt | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex add6f43..0356d25 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -17,24 +17,24 @@ options are:\n \tThe name of the file to be read in each directory for excluded\n \tfiles (typically `.gitignore`).\n \n-`collect_ignored`::\n+`flags`::\n \n-\tInclude paths that are to be excluded in the result.\n+\tA bit-field of options:\n \n-`show_ignored`::\n+`DIR_SHOW_IGNORED`:::\n \n \tThe traversal is for finding just ignored files, not unignored\n \tfiles.\n \n-`show_other_directories`::\n+`DIR_SHOW_OTHER_DIRECTORIES`:::\n \n \tInclude a directory that is not tracked.\n \n-`hide_empty_directories`::\n+`DIR_HIDE_EMPTY_DIRECTORIES`:::\n \n \tDo not include a directory that is not tracked and is empty.\n \n-`no_gitlinks`::\n+`DIR_NO_GITLINKS`:::\n \n \tIf set, recurse into a directory that looks like a git\n \tdirectory.  Otherwise it is shown as a directory.\n-- \n1.7.12.155.ge5750d5.dirty\n"},{"id":"198216","messageId":"1346544731-938-3-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1346544731-938-1-git-send-email-git@adamspiers.org","subject":"[PATCH 2/9] Improve documentation and comments regarding directory traversal API","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-02T00:12:04Z","receivedAt":"2012-09-02T00:12:04Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"Signed-off-by: Adam Spiers <git@adamspiers.org>\n---\n Documentation/technical/api-directory-listing.txt |  9 +++++---\n dir.c                                             |  8 ++++++-\n dir.h                                             | 26 +++++++++++++++++++++--\n 3 files changed, 37 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex 0356d25..944fc39 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -9,8 +9,11 @@ Data structure\n --------------\n \n `struct dir_struct` structure is used to pass directory traversal\n-options to the library and to record the paths discovered.  The notable\n-options are:\n+options to the library and to record the paths discovered.  A single\n+`struct dir_struct` is used regardless of whether or not the traversal\n+recursively descends into subdirectories.\n+\n+The notable options are:\n \n `exclude_per_dir`::\n \n@@ -39,7 +42,7 @@ options are:\n \tIf set, recurse into a directory that looks like a git\n \tdirectory.  Otherwise it is shown as a directory.\n \n-The result of the enumeration is left in these fields::\n+The result of the enumeration is left in these fields:\n \n `entries[]`::\n \ndiff --git a/dir.c b/dir.c\nindex 240bf0c..c9f341a 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -2,6 +2,8 @@\n  * This handles recursive filename detection with exclude\n  * files, index knowledge etc..\n  *\n+ * See Documentation/technical/api-directory-listing.txt\n+ *\n  * Copyright (C) Linus Torvalds, 2005-2006\n  *\t\t Junio Hamano, 2005-2006\n  */\n@@ -449,6 +451,10 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)\n \t\tdie(\"cannot use %s as an exclude file\", fname);\n }\n \n+/*\n+ * Loads the per-directory exclude list for the substring of base\n+ * which has a char length of baselen.\n+ */\n static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n {\n \tstruct exclude_list *el;\n@@ -459,7 +465,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t    (baselen + strlen(dir->exclude_per_dir) >= PATH_MAX))\n \t\treturn; /* too long a path -- ignore */\n \n-\t/* Pop the ones that are not the prefix of the path being checked. */\n+\t/* Pop the directories that are not the prefix of the path being checked. */\n \tel = &dir->exclude_list[EXC_DIRS];\n \twhile ((stk = dir->exclude_stack) != NULL) {\n \t\tif (stk->baselen <= baselen &&\ndiff --git a/dir.h b/dir.h\nindex 893465a..a226fbc 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -1,6 +1,8 @@\n #ifndef DIR_H\n #define DIR_H\n \n+/* See Documentation/technical/api-directory-listing.txt */\n+\n #include \"strbuf.h\"\n \n struct dir_entry {\n@@ -12,6 +14,12 @@ struct dir_entry {\n #define EXC_FLAG_ENDSWITH 4\n #define EXC_FLAG_MUSTBEDIR 8\n \n+/*\n+ * Each .gitignore file will be parsed into patterns which are then\n+ * appended to the relevant exclude_list (either EXC_DIRS or\n+ * EXC_FILE).  exclude_lists are also used to represent the list of\n+ * --exclude values passed via CLI args (EXC_CMDL).\n+ */\n struct exclude_list {\n \tint nr;\n \tint alloc;\n@@ -26,9 +34,15 @@ struct exclude_list {\n \t} **excludes;\n };\n \n+/*\n+ * The contents of the per-directory exclude files are lazily read on\n+ * demand and then cached in memory, one per exclude_stack struct, in\n+ * order to avoid opening and parsing each one every time that\n+ * directory is traversed.\n+ */\n struct exclude_stack {\n-\tstruct exclude_stack *prev;\n-\tchar *filebuf;\n+\tstruct exclude_stack *prev; /* the struct exclude_stack for the parent directory */\n+\tchar *filebuf; /* remember pointer to per-directory exclude file contents so we can free() */\n \tint baselen;\n \tint exclude_ix;\n };\n@@ -59,6 +73,14 @@ struct dir_struct {\n #define EXC_DIRS 1\n #define EXC_FILE 2\n \n+\t/*\n+\t * Temporary variables which are used during loading of the\n+\t * per-directory exclude lists.\n+\t *\n+\t * exclude_stack points to the top of the exclude_stack, and\n+\t * basebuf contains the full path to the current\n+\t * (sub)directory in the traversal.\n+\t */\n \tstruct exclude_stack *exclude_stack;\n \tchar basebuf[PATH_MAX];\n };\n-- \n1.7.12.155.ge5750d5.dirty\n"},{"id":"198211","messageId":"1346544731-938-4-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1346544731-938-1-git-send-email-git@adamspiers.org","subject":"[PATCH 3/9] Rename cryptic 'which' variable to more consistent name","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-02T00:12:05Z","receivedAt":"2012-09-02T00:12:05Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"'el' is only *slightly* less cryptic, but is already used as the\nvariable name for a struct exclude_list pointer in numerous other\nplaces, so this reduces the number of cryptic variable names in use by\none :-)\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n dir.c | 10 +++++-----\n dir.h |  4 ++--\n 2 files changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex c9f341a..57a5d11 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -311,7 +311,7 @@ static int no_wildcard(const char *string)\n }\n \n void add_exclude(const char *string, const char *base,\n-\t\t int baselen, struct exclude_list *which)\n+\t\t int baselen, struct exclude_list *el)\n {\n \tstruct exclude *x;\n \tsize_t len;\n@@ -346,8 +346,8 @@ void add_exclude(const char *string, const char *base,\n \tx->nowildcardlen = simple_length(string);\n \tif (*string == '*' && no_wildcard(string+1))\n \t\tx->flags |= EXC_FLAG_ENDSWITH;\n-\tALLOC_GROW(which->excludes, which->nr + 1, which->alloc);\n-\twhich->excludes[which->nr++] = x;\n+\tALLOC_GROW(el->excludes, el->nr + 1, el->alloc);\n+\tel->excludes[el->nr++] = x;\n }\n \n static void *read_skip_worktree_file_from_index(const char *path, size_t *size)\n@@ -389,7 +389,7 @@ int add_excludes_from_file_to_list(const char *fname,\n \t\t\t\t   const char *base,\n \t\t\t\t   int baselen,\n \t\t\t\t   char **buf_p,\n-\t\t\t\t   struct exclude_list *which,\n+\t\t\t\t   struct exclude_list *el,\n \t\t\t\t   int check_index)\n {\n \tstruct stat st;\n@@ -436,7 +436,7 @@ int add_excludes_from_file_to_list(const char *fname,\n \t\tif (buf[i] == '\\n') {\n \t\t\tif (entry != buf + i && entry[0] != '#') {\n \t\t\t\tbuf[i - (i && buf[i-1] == '\\r')] = 0;\n-\t\t\t\tadd_exclude(entry, base, baselen, which);\n+\t\t\t\tadd_exclude(entry, base, baselen, el);\n \t\t\t}\n \t\t\tentry = buf + i + 1;\n \t\t}\ndiff --git a/dir.h b/dir.h\nindex a226fbc..549a187 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -117,10 +117,10 @@ extern int path_excluded(struct path_exclude_check *, const char *, int namelen,\n \n \n extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,\n-\t\t\t\t\t  char **buf_p, struct exclude_list *which, int check_index);\n+\t\t\t\t\t  char **buf_p, struct exclude_list *el, int check_index);\n extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n extern void add_exclude(const char *string, const char *base,\n-\t\t\tint baselen, struct exclude_list *which);\n+\t\t\tint baselen, struct exclude_list *el);\n extern void free_excludes(struct exclude_list *el);\n extern int file_exists(const char *);\n \n-- \n1.7.12.155.ge5750d5.dirty\n"},{"id":"198218","messageId":"1346544731-938-5-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1346544731-938-1-git-send-email-git@adamspiers.org","subject":"[PATCH 4/9] Refactor excluded_from_list","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-02T00:12:06Z","receivedAt":"2012-09-02T00:12:06Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"The excluded function uses a new helper function called\nexclude_from_list_1() to perform the inner loop over all of the\nexclude patterns.  The helper just tells us whether the path is\nincluded, excluded, or undecided.\n\nHowever, it may be useful to know _which_ pattern was\ntriggered.  So let's pass out the entire exclude match,\nwhich contains the status information we were already\npassing out.\n\nFurther patches can make use of this.\n\nThis is a modified forward port of a patch from 2009 by Jeff King:\nhttp://article.gmane.org/gmane.comp.version-control.git/108815\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n dir.c | 40 +++++++++++++++++++++++++++++-----------\n 1 file changed, 29 insertions(+), 11 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 57a5d11..3a532d5 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -509,22 +509,24 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \tdir->basebuf[baselen] = '\\0';\n }\n \n-/* Scan the list and let the last match determine the fate.\n- * Return 1 for exclude, 0 for include and -1 for undecided.\n+/*\n+ * Scan the given exclude list in reverse to see whether pathname\n+ * should be ignored.  The first match (i.e. the last on the list), if\n+ * any, determines the fate.  Returns the exclude_list element which\n+ * matched, or NULL for undecided.\n  */\n-int excluded_from_list(const char *pathname,\n-\t\t       int pathlen, const char *basename, int *dtype,\n-\t\t       struct exclude_list *el)\n+struct exclude *excluded_from_list_1(const char *pathname, int pathlen,\n+\t\t\t\t     const char *basename, int *dtype,\n+\t\t\t\t     struct exclude_list *el)\n {\n \tint i;\n \n \tif (!el->nr)\n-\t\treturn -1;\t/* undefined */\n+\t\treturn NULL;\t/* undefined */\n \n \tfor (i = el->nr - 1; 0 <= i; i--) {\n \t\tstruct exclude *x = el->excludes[i];\n \t\tconst char *name, *exclude = x->pattern;\n-\t\tint to_exclude = x->to_exclude;\n \t\tint namelen, prefix = x->nowildcardlen;\n \n \t\tif (x->flags & EXC_FLAG_MUSTBEDIR) {\n@@ -538,14 +540,14 @@ int excluded_from_list(const char *pathname,\n \t\t\t/* match basename */\n \t\t\tif (prefix == x->patternlen) {\n \t\t\t\tif (!strcmp_icase(exclude, basename))\n-\t\t\t\t\treturn to_exclude;\n+\t\t\t\t\treturn x;\n \t\t\t} else if (x->flags & EXC_FLAG_ENDSWITH) {\n \t\t\t\tif (x->patternlen - 1 <= pathlen &&\n \t\t\t\t    !strcmp_icase(exclude + 1, pathname + pathlen - x->patternlen + 1))\n-\t\t\t\t\treturn to_exclude;\n+\t\t\t\t\treturn x;\n \t\t\t} else {\n \t\t\t\tif (fnmatch_icase(exclude, basename, 0) == 0)\n-\t\t\t\t\treturn to_exclude;\n+\t\t\t\t\treturn x;\n \t\t\t}\n \t\t\tcontinue;\n \t\t}\n@@ -580,7 +582,23 @@ int excluded_from_list(const char *pathname,\n \t\t}\n \n \t\tif (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))\n-\t\t\treturn to_exclude;\n+\t\t\treturn x;\n+\t}\n+\treturn NULL; /* undecided */\n+}\n+\n+/*\n+ * Scan the list and let the last match determine the fate.\n+ * Return 1 for exclude, 0 for include and -1 for undecided.\n+ */\n+int excluded_from_list(const char *pathname,\n+\t\t       int pathlen, const char *basename, int *dtype,\n+\t\t       struct exclude_list *el)\n+{\n+\tstruct exclude *exclude;\n+\texclude = excluded_from_list_1(pathname, pathlen, basename, dtype, el);\n+\tif (exclude) {\n+\t\treturn exclude->to_exclude;\n \t}\n \treturn -1; /* undecided */\n }\n-- \n1.7.12.155.ge5750d5.dirty\n"},{"id":"198214","messageId":"1346544731-938-6-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1346544731-938-1-git-send-email-git@adamspiers.org","subject":"[PATCH 5/9] Refactor excluded and path_excluded","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-02T00:12:07Z","receivedAt":"2012-09-02T00:12:07Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"In a similar way to the previous commit, this extracts new helper\nfunctions excluded_1() and path_excluded_1() which return the last\nexclude_list element which matched, or NULL if no match was found.\nexcluded() and path_excluded() become wrappers around these, and just\nreturn 0 or 1 depending on whether any matching exclude_list element\nwas found.\n\nThis allows callers to find out _why_ a given path was excluded,\nrather than just whether it was or not, paving the way for a new git\nsub-command which allows users to test their exclude lists from the\ncommand line.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n dir.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------------\n dir.h |  3 +++\n 2 files changed, 67 insertions(+), 17 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 3a532d5..3d438c3 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -603,23 +603,42 @@ int excluded_from_list(const char *pathname,\n \treturn -1; /* undecided */\n }\n \n-static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n+/*\n+ * Loads the exclude lists for the directory containing pathname, then\n+ * scans all exclude lists to determine whether pathname is excluded.\n+ * Returns the exclude_list element which matched, or NULL for\n+ * undecided.\n+ */\n+static struct exclude *excluded_1(struct dir_struct *dir, const char *pathname, int *dtype_p)\n {\n \tint pathlen = strlen(pathname);\n \tint st;\n+\tstruct exclude *exclude;\n \tconst char *basename = strrchr(pathname, '/');\n \tbasename = (basename) ? basename+1 : pathname;\n \n \tprep_exclude(dir, pathname, basename-pathname);\n \tfor (st = EXC_CMDL; st <= EXC_FILE; st++) {\n-\t\tswitch (excluded_from_list(pathname, pathlen, basename,\n-\t\t\t\t\t   dtype_p, &dir->exclude_list[st])) {\n-\t\tcase 0:\n-\t\t\treturn 0;\n-\t\tcase 1:\n-\t\t\treturn 1;\n+\t\texclude = excluded_from_list_1(pathname, pathlen, basename,\n+\t\t\t\t\t       dtype_p, &dir->exclude_list[st]);\n+\t\tif (exclude) {\n+\t\t\treturn exclude;\n \t\t}\n \t}\n+\treturn NULL;\n+}\n+\n+/*\n+ * Loads the exclude lists for the directory containing pathname, then\n+ * scans all exclude lists to determine whether pathname is excluded.\n+ * Returns 1 if true, otherwise 0.\n+ */\n+static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n+{\n+\tstruct exclude *exclude = excluded_1(dir, pathname, dtype_p);\n+\tif (exclude) {\n+\t\treturn exclude->to_exclude;\n+\t}\n \treturn 0;\n }\n \n@@ -627,6 +646,7 @@ void path_exclude_check_init(struct path_exclude_check *check,\n \t\t\t     struct dir_struct *dir)\n {\n \tcheck->dir = dir;\n+\tcheck->exclude = NULL;\n \tstrbuf_init(&check->path, 256);\n }\n \n@@ -636,18 +656,20 @@ void path_exclude_check_clear(struct path_exclude_check *check)\n }\n \n /*\n- * Is this name excluded?  This is for a caller like show_files() that\n- * do not honor directory hierarchy and iterate through paths that are\n- * possibly in an ignored directory.\n+ * For each subdirectory in name, starting with the top-most, checks\n+ * to see if that subdirectory is excluded, and if so, returns the\n+ * corresponding exclude structure.  Otherwise, checks whether name\n+ * itself (which is presumably a file) is excluded.\n  *\n  * A path to a directory known to be excluded is left in check->path to\n  * optimize for repeated checks for files in the same excluded directory.\n  */\n-int path_excluded(struct path_exclude_check *check,\n-\t\t  const char *name, int namelen, int *dtype)\n+struct exclude *path_excluded_1(struct path_exclude_check *check,\n+\t\t\t\tconst char *name, int namelen, int *dtype)\n {\n \tint i;\n \tstruct strbuf *path = &check->path;\n+\tstruct exclude *exclude;\n \n \t/*\n \t * we allow the caller to pass namelen as an optimization; it\n@@ -657,11 +679,18 @@ int path_excluded(struct path_exclude_check *check,\n \tif (namelen < 0)\n \t\tnamelen = strlen(name);\n \n+\t/*\n+\t * If path is non-empty, and name is equal to path or a\n+\t * subdirectory of path, name should be excluded, because\n+\t * it's inside a directory which is already known to be\n+\t * excluded and was previously left in check->path.\n+\t */\n \tif (path->len &&\n \t    path->len <= namelen &&\n \t    !memcmp(name, path->buf, path->len) &&\n-\t    (!name[path->len] || name[path->len] == '/'))\n-\t\treturn 1;\n+\t    (!name[path->len] || name[path->len] == '/')) {\n+\t\treturn check->exclude;\n+\t}\n \n \tstrbuf_setlen(path, 0);\n \tfor (i = 0; name[i]; i++) {\n@@ -669,8 +698,11 @@ int path_excluded(struct path_exclude_check *check,\n \n \t\tif (ch == '/') {\n \t\t\tint dt = DT_DIR;\n-\t\t\tif (excluded(check->dir, path->buf, &dt))\n-\t\t\t\treturn 1;\n+\t\t\texclude = excluded_1(check->dir, path->buf, &dt);\n+\t\t\tif (exclude) {\n+\t\t\t\tcheck->exclude = exclude;\n+\t\t\t\treturn exclude;\n+\t\t\t}\n \t\t}\n \t\tstrbuf_addch(path, ch);\n \t}\n@@ -678,7 +710,22 @@ int path_excluded(struct path_exclude_check *check,\n \t/* An entry in the index; cannot be a directory with subentries */\n \tstrbuf_setlen(path, 0);\n \n-\treturn excluded(check->dir, name, dtype);\n+\treturn excluded_1(check->dir, name, dtype);\n+}\n+\n+/*\n+ * Is this name excluded?  This is for a caller like show_files() that\n+ * do not honor directory hierarchy and iterate through paths that are\n+ * possibly in an ignored directory.\n+ */\n+int path_excluded(struct path_exclude_check *check,\n+\t\t  const char *name, int namelen, int *dtype)\n+{\n+\tstruct exclude *exclude = path_excluded_1(check, name, namelen, dtype);\n+\tif (exclude) {\n+\t\treturn exclude->to_exclude;\n+\t}\n+\treturn 0;\n }\n \n static struct dir_entry *dir_entry_new(const char *pathname, int len)\ndiff --git a/dir.h b/dir.h\nindex 549a187..1b4f9dc 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -109,10 +109,13 @@ struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname,\n  */\n struct path_exclude_check {\n \tstruct dir_struct *dir;\n+\tstruct exclude *exclude;\n \tstruct strbuf path;\n };\n extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *);\n extern void path_exclude_check_clear(struct path_exclude_check *);\n+extern struct exclude *path_excluded_1(struct path_exclude_check *, const char *,\n+\t\t\t\t       int namelen, int *dtype);\n extern int path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);\n \n \n-- \n1.7.12.155.ge5750d5.dirty\n"},{"id":"198213","messageId":"1346544731-938-7-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1346544731-938-1-git-send-email-git@adamspiers.org","subject":"[PATCH 6/9] For each exclude pattern, store information about where it came from","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-02T00:12:08Z","receivedAt":"2012-09-02T00:12:08Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"For exclude patterns read in from files, the filename is stored together\nwith the corresponding line number (counting starting at 1).\n\nFor exclude patterns provided on the command line, the sequence number\nis negative, with counting starting at -1, so for example the 2nd\npattern provided via --exclude would be numbered -2.  This allows any\nfuture consumers of that data to easily distinguish between exclude\npatterns from files vs. from the CLI.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n builtin/clean.c    |  2 +-\n builtin/ls-files.c |  3 ++-\n dir.c              | 25 +++++++++++++++++++------\n dir.h              |  5 ++++-\n 4 files changed, 26 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/clean.c b/builtin/clean.c\nindex 0c7b3d0..f618231 100644\n--- a/builtin/clean.c\n+++ b/builtin/clean.c\n@@ -99,7 +99,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)\n \n \tfor (i = 0; i < exclude_list.nr; i++)\n \t\tadd_exclude(exclude_list.items[i].string, \"\", 0,\n-\t\t\t    &dir.exclude_list[EXC_CMDL]);\n+\t\t\t    &dir.exclude_list[EXC_CMDL], \"--exclude option\", -(i+1));\n \n \tpathspec = get_pathspec(prefix, argv);\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 31b3f2d..420ff40 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -35,6 +35,7 @@ static int error_unmatch;\n static char *ps_matched;\n static const char *with_tree;\n static int exc_given;\n+static int exclude_args = 0;\n \n static const char *tag_cached = \"\";\n static const char *tag_unmerged = \"\";\n@@ -423,7 +424,7 @@ static int option_parse_exclude(const struct option *opt,\n \tstruct exclude_list *list = opt->value;\n \n \texc_given = 1;\n-\tadd_exclude(arg, \"\", 0, list);\n+\tadd_exclude(arg, \"\", 0, list, \"--exclude option\", --exclude_args);\n \n \treturn 0;\n }\ndiff --git a/dir.c b/dir.c\nindex 3d438c3..ac8c838 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -311,7 +311,7 @@ static int no_wildcard(const char *string)\n }\n \n void add_exclude(const char *string, const char *base,\n-\t\t int baselen, struct exclude_list *el)\n+\t\t int baselen, struct exclude_list *el, const char *src, int srcpos)\n {\n \tstruct exclude *x;\n \tsize_t len;\n@@ -341,6 +341,8 @@ void add_exclude(const char *string, const char *base,\n \tx->base = base;\n \tx->baselen = baselen;\n \tx->flags = flags;\n+\tx->src = src;\n+\tx->srcpos = srcpos;\n \tif (!strchr(string, '/'))\n \t\tx->flags |= EXC_FLAG_NODIR;\n \tx->nowildcardlen = simple_length(string);\n@@ -393,7 +395,7 @@ int add_excludes_from_file_to_list(const char *fname,\n \t\t\t\t   int check_index)\n {\n \tstruct stat st;\n-\tint fd, i;\n+\tint fd, i, lineno = 1;\n \tsize_t size = 0;\n \tchar *buf, *entry;\n \n@@ -436,8 +438,9 @@ int add_excludes_from_file_to_list(const char *fname,\n \t\tif (buf[i] == '\\n') {\n \t\t\tif (entry != buf + i && entry[0] != '#') {\n \t\t\t\tbuf[i - (i && buf[i-1] == '\\r')] = 0;\n-\t\t\t\tadd_exclude(entry, base, baselen, el);\n+\t\t\t\tadd_exclude(entry, base, baselen, el, fname, lineno);\n \t\t\t}\n+\t\t\tlineno++;\n \t\t\tentry = buf + i + 1;\n \t\t}\n \t}\n@@ -472,8 +475,10 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t\t    !strncmp(dir->basebuf, base, stk->baselen))\n \t\t\tbreak;\n \t\tdir->exclude_stack = stk->prev;\n-\t\twhile (stk->exclude_ix < el->nr)\n-\t\t\tfree(el->excludes[--el->nr]);\n+\t\twhile (stk->exclude_ix < el->nr) {\n+\t\t\tstruct exclude *exclude = el->excludes[--el->nr];\n+\t\t\tfree(exclude);\n+\t\t}\n \t\tfree(stk->filebuf);\n \t\tfree(stk);\n \t}\n@@ -500,7 +505,15 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t\tmemcpy(dir->basebuf + current, base + current,\n \t\t       stk->baselen - current);\n \t\tstrcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);\n-\t\tadd_excludes_from_file_to_list(dir->basebuf,\n+\n+\t\t/* dir->basebuf gets reused by the traversal, but we\n+\t\t * need fname to remain unchanged to ensure the src\n+\t\t * member of each struct exclude correctly back-references\n+\t\t * its source file.\n+\t\t */\n+\t\tchar *fname = strdup(dir->basebuf);\n+\n+\t\tadd_excludes_from_file_to_list(fname,\n \t\t\t\t\t       dir->basebuf, stk->baselen,\n \t\t\t\t\t       &stk->filebuf, el, 1);\n \t\tdir->exclude_stack = stk;\ndiff --git a/dir.h b/dir.h\nindex 1b4f9dc..81efee4 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -31,6 +31,9 @@ struct exclude_list {\n \t\tint baselen;\n \t\tint to_exclude;\n \t\tint flags;\n+\t\tconst char *src;\n+\t\tint srcpos; /* counting starts from 1 for line numbers in ignore files,\n+\t\t\t       and from -1 decrementing for patterns from CLI (--exclude) */\n \t} **excludes;\n };\n \n@@ -123,7 +126,7 @@ extern int add_excludes_from_file_to_list(const char *fname, const char *base, i\n \t\t\t\t\t  char **buf_p, struct exclude_list *el, int check_index);\n extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n extern void add_exclude(const char *string, const char *base,\n-\t\t\tint baselen, struct exclude_list *el);\n+\t\t\tint baselen, struct exclude_list *el, const char *src, int srcpos);\n extern void free_excludes(struct exclude_list *el);\n extern int file_exists(const char *);\n \n-- \n1.7.12.155.ge5750d5.dirty\n"},{"id":"198212","messageId":"1346544731-938-8-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1346544731-938-1-git-send-email-git@adamspiers.org","subject":"[PATCH 7/9] Extract some useful pathspec handling code from builtin/add.c into a library","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-02T00:12:09Z","receivedAt":"2012-09-02T00:12:09Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"This is in preparation for reuse by a new git check-ignore command.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n Makefile      |  2 ++\n builtin/add.c | 82 +++----------------------------------------------------\n pathspec.c    | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n pathspec.h    |  6 +++++\n 4 files changed, 98 insertions(+), 79 deletions(-)\n create mode 100644 pathspec.c\n create mode 100644 pathspec.h\n\ndiff --git a/Makefile b/Makefile\nindex 66e8216..272ab69 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -640,6 +640,7 @@ LIB_H += pack-revindex.h\n LIB_H += pack.h\n LIB_H += parse-options.h\n LIB_H += patch-ids.h\n+LIB_H += pathspec.h\n LIB_H += pkt-line.h\n LIB_H += progress.h\n LIB_H += prompt.h\n@@ -760,6 +761,7 @@ LIB_OBJS += parse-options-cb.o\n LIB_OBJS += patch-delta.o\n LIB_OBJS += patch-ids.o\n LIB_OBJS += path.o\n+LIB_OBJS += pathspec.o\n LIB_OBJS += pkt-line.o\n LIB_OBJS += preload-index.o\n LIB_OBJS += pretty.o\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 89dce56..a7ed2ad 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -6,6 +6,7 @@\n #include \"cache.h\"\n #include \"builtin.h\"\n #include \"dir.h\"\n+#include \"pathspec.h\"\n #include \"exec_cmd.h\"\n #include \"cache-tree.h\"\n #include \"run-command.h\"\n@@ -97,39 +98,6 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)\n \treturn !!data.add_errors;\n }\n \n-static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)\n-{\n-\tint num_unmatched = 0, i;\n-\n-\t/*\n-\t * Since we are walking the index as if we were walking the directory,\n-\t * we have to mark the matched pathspec as seen; otherwise we will\n-\t * mistakenly think that the user gave a pathspec that did not match\n-\t * anything.\n-\t */\n-\tfor (i = 0; i < specs; i++)\n-\t\tif (!seen[i])\n-\t\t\tnum_unmatched++;\n-\tif (!num_unmatched)\n-\t\treturn;\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tstruct cache_entry *ce = active_cache[i];\n-\t\tmatch_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);\n-\t}\n-}\n-\n-static char *find_used_pathspec(const char **pathspec)\n-{\n-\tchar *seen;\n-\tint i;\n-\n-\tfor (i = 0; pathspec[i];  i++)\n-\t\t; /* just counting */\n-\tseen = xcalloc(i, 1);\n-\tfill_pathspec_matches(pathspec, seen, i);\n-\treturn seen;\n-}\n-\n static char *prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)\n {\n \tchar *seen;\n@@ -153,33 +121,6 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, int\n \treturn seen;\n }\n \n-static void treat_gitlinks(const char **pathspec)\n-{\n-\tint i;\n-\n-\tif (!pathspec || !*pathspec)\n-\t\treturn;\n-\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tstruct cache_entry *ce = active_cache[i];\n-\t\tif (S_ISGITLINK(ce->ce_mode)) {\n-\t\t\tint len = ce_namelen(ce), j;\n-\t\t\tfor (j = 0; pathspec[j]; j++) {\n-\t\t\t\tint len2 = strlen(pathspec[j]);\n-\t\t\t\tif (len2 <= len || pathspec[j][len] != '/' ||\n-\t\t\t\t    memcmp(ce->name, pathspec[j], len))\n-\t\t\t\t\tcontinue;\n-\t\t\t\tif (len2 == len + 1)\n-\t\t\t\t\t/* strip trailing slash */\n-\t\t\t\t\tpathspec[j] = xstrndup(ce->name, len);\n-\t\t\t\telse\n-\t\t\t\t\tdie (_(\"Path '%s' is in submodule '%.*s'\"),\n-\t\t\t\t\t\tpathspec[j], len, ce->name);\n-\t\t\t}\n-\t\t}\n-\t}\n-}\n-\n static void refresh(int verbose, const char **pathspec)\n {\n \tchar *seen;\n@@ -197,23 +138,6 @@ static void refresh(int verbose, const char **pathspec)\n         free(seen);\n }\n \n-static const char **validate_pathspec(int argc, const char **argv, const char *prefix)\n-{\n-\tconst char **pathspec = get_pathspec(prefix, argv);\n-\n-\tif (pathspec) {\n-\t\tconst char **p;\n-\t\tfor (p = pathspec; *p; p++) {\n-\t\t\tif (has_symlink_leading_path(*p, strlen(*p))) {\n-\t\t\t\tint len = prefix ? strlen(prefix) : 0;\n-\t\t\t\tdie(_(\"'%s' is beyond a symbolic link\"), *p + len);\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\treturn pathspec;\n-}\n-\n int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\tconst char **pathspec)\n {\n@@ -248,7 +172,7 @@ int interactive_add(int argc, const char **argv, const char *prefix, int patch)\n \tconst char **pathspec = NULL;\n \n \tif (argc) {\n-\t\tpathspec = validate_pathspec(argc, argv, prefix);\n+\t\tpathspec = validate_pathspec(prefix, argv);\n \t\tif (!pathspec)\n \t\t\treturn -1;\n \t}\n@@ -414,7 +338,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \t\tfprintf(stderr, _(\"Maybe you wanted to say 'git add .'?\\n\"));\n \t\treturn 0;\n \t}\n-\tpathspec = validate_pathspec(argc, argv, prefix);\n+\tpathspec = validate_pathspec(prefix, argv);\n \n \tif (read_cache() < 0)\n \t\tdie(_(\"index file corrupt\"));\ndiff --git a/pathspec.c b/pathspec.c\nnew file mode 100644\nindex 0000000..51cb8ce\n--- /dev/null\n+++ b/pathspec.c\n@@ -0,0 +1,87 @@\n+#include \"cache.h\"\n+#include \"dir.h\"\n+\n+void validate_path(const char *prefix, const char *path)\n+{\n+\tif (has_symlink_leading_path(path, strlen(path))) {\n+\t\tint len = prefix ? strlen(prefix) : 0;\n+\t\tdie(_(\"'%s' is beyond a symbolic link\"), path + len);\n+\t}\n+}\n+\n+const char **validate_pathspec(const char *prefix, const char **files)\n+{\n+\tconst char **pathspec = get_pathspec(prefix, files);\n+\n+\tif (pathspec) {\n+\t\tconst char **p;\n+\t\tfor (p = pathspec; *p; p++) {\n+\t\t\tvalidate_path(prefix, *p);\n+\t\t}\n+\t}\n+\n+\treturn pathspec;\n+}\n+\n+void fill_pathspec_matches(const char **pathspec, char *seen, int specs)\n+{\n+\tint num_unmatched = 0, i;\n+\n+\t/*\n+\t * Since we are walking the index as if we were walking the directory,\n+\t * we have to mark the matched pathspec as seen; otherwise we will\n+\t * mistakenly think that the user gave a pathspec that did not match\n+\t * anything.\n+\t */\n+\tfor (i = 0; i < specs; i++)\n+\t\tif (!seen[i])\n+\t\t\tnum_unmatched++;\n+\tif (!num_unmatched)\n+\t\treturn;\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tmatch_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);\n+\t}\n+}\n+\n+char *find_used_pathspec(const char **pathspec)\n+{\n+\tchar *seen;\n+\tint i;\n+\n+\tfor (i = 0; pathspec[i];  i++)\n+\t\t; /* just counting */\n+\tseen = xcalloc(i, 1);\n+\tfill_pathspec_matches(pathspec, seen, i);\n+\treturn seen;\n+}\n+\n+void treat_gitlink(const char *path)\n+{\n+\tint i, len = strlen(path);\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tif (S_ISGITLINK(ce->ce_mode)) {\n+\t\t\tint len2 = ce_namelen(ce);\n+\t\t\tif (len <= len2 || path[len2] != '/' ||\n+\t\t\t    memcmp(ce->name, path, len2))\n+\t\t\t\tcontinue;\n+\t\t\tif (len == len2 + 1)\n+\t\t\t\t/* strip trailing slash */\n+\t\t\t\tpath = xstrndup(ce->name, len2);\n+\t\t\telse\n+\t\t\t\tdie (_(\"Path '%s' is in submodule '%.*s'\"),\n+\t\t\t\t     path, len2, ce->name);\n+\t\t}\n+\t}\n+}\n+\n+void treat_gitlinks(const char **pathspec)\n+{\n+\tif (!pathspec || !*pathspec)\n+\t\treturn;\n+\n+\tint i;\n+\tfor (i = 0; pathspec[i]; i++)\n+\t\ttreat_gitlink(pathspec[i]);\n+}\ndiff --git a/pathspec.h b/pathspec.h\nnew file mode 100644\nindex 0000000..87e4296\n--- /dev/null\n+++ b/pathspec.h\n@@ -0,0 +1,6 @@\n+extern void validate_path(const char *prefix, const char *path);\n+extern const char **validate_pathspec(const char *prefix, const char **files);\n+extern char *find_used_pathspec(const char **pathspec);\n+extern void fill_pathspec_matches(const char **pathspec, char *seen, int specs);\n+extern void treat_gitlink(const char *path);\n+extern void treat_gitlinks(const char **pathspec);\n-- \n1.7.12.155.ge5750d5.dirty\n"},{"id":"198220","messageId":"1346544731-938-9-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1346544731-938-1-git-send-email-git@adamspiers.org","subject":"[PATCH 8/9] Provide free_directory() for reclaiming dir_struct memory","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-02T00:12:10Z","receivedAt":"2012-09-02T00:12:10Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"Signed-off-by: Adam Spiers <git@adamspiers.org>\n---\n Documentation/technical/api-directory-listing.txt |  2 ++\n dir.c                                             | 23 +++++++++++++++++++++--\n dir.h                                             |  1 +\n 3 files changed, 24 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex 944fc39..e339c18 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -79,4 +79,6 @@ marked. If you to exclude files, make sure you have loaded index first.\n \n * Use `dir.entries[]`.\n \n+* Call `free_directory()` when none of the contained elements are no longer in use.\n+\n (JC)\ndiff --git a/dir.c b/dir.c\nindex ac8c838..80f9b22 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -454,6 +454,12 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)\n \t\tdie(\"cannot use %s as an exclude file\", fname);\n }\n \n+static void free_exclude_stack(struct exclude_stack *stk)\n+{\n+\tfree(stk->filebuf);\n+\tfree(stk);\n+}\n+\n /*\n  * Loads the per-directory exclude list for the substring of base\n  * which has a char length of baselen.\n@@ -479,8 +485,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t\t\tstruct exclude *exclude = el->excludes[--el->nr];\n \t\t\tfree(exclude);\n \t\t}\n-\t\tfree(stk->filebuf);\n-\t\tfree(stk);\n+\t\tfree_exclude_stack(stk);\n \t}\n \n \t/* Read from the parent directories and push them down. */\n@@ -1467,3 +1472,17 @@ void free_pathspec(struct pathspec *pathspec)\n \tfree(pathspec->items);\n \tpathspec->items = NULL;\n }\n+\n+void free_directory(struct dir_struct *dir)\n+{\n+\tint st;\n+\tfor (st = EXC_CMDL; st <= EXC_FILE; st++)\n+\t\tfree_excludes(&dir->exclude_list[st]);\n+\n+\tstruct exclude_stack *prev, *stk = dir->exclude_stack;\n+\twhile (stk) {\n+\t\tprev = stk->prev;\n+\t\tfree_exclude_stack(stk);\n+\t\tstk = prev;\n+\t}\n+}\ndiff --git a/dir.h b/dir.h\nindex 81efee4..f7cea9c 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -128,6 +128,7 @@ extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n extern void add_exclude(const char *string, const char *base,\n \t\t\tint baselen, struct exclude_list *el, const char *src, int srcpos);\n extern void free_excludes(struct exclude_list *el);\n+extern void free_directory(struct dir_struct *dir);\n extern int file_exists(const char *);\n \n extern int is_inside_dir(const char *dir);\n-- \n1.7.12.155.ge5750d5.dirty\n"},{"id":"198215","messageId":"1346544731-938-10-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1346544731-938-1-git-send-email-git@adamspiers.org","subject":"[PATCH 9/9] Add git-check-ignores","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-02T00:12:11Z","receivedAt":"2012-09-02T00:12:11Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"This works in a similar manner to git-check-attr.  Some code\nwas reused from add.c by refactoring out into pathspec.c.\n\nThanks to Jeff King and Junio C Hamano for the idea:\nhttp://thread.gmane.org/gmane.comp.version-control.git/108671/focus=108815\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n .gitignore                             |   1 +\n Documentation/git-check-ignore.txt     |  58 +++++++\n Documentation/gitignore.txt            |   6 +-\n Makefile                               |   1 +\n builtin.h                              |   1 +\n builtin/add.c                          |   2 +-\n builtin/check-ignore.c                 | 150 ++++++++++++++++\n command-list.txt                       |   1 +\n contrib/completion/git-completion.bash |   1 +\n git.c                                  |   1 +\n t/t0007-ignores.sh                     | 301 +++++++++++++++++++++++++++++++++\n 11 files changed, 520 insertions(+), 3 deletions(-)\n create mode 100644 Documentation/git-check-ignore.txt\n create mode 100644 builtin/check-ignore.c\n create mode 100755 t/t0007-ignores.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex bb5c91e..0cbe94c 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -22,6 +22,7 @@\n /git-bundle\n /git-cat-file\n /git-check-attr\n+/git-check-ignore\n /git-check-ref-format\n /git-checkout\n /git-checkout-index\ndiff --git a/Documentation/git-check-ignore.txt b/Documentation/git-check-ignore.txt\nnew file mode 100644\nindex 0000000..3a85dbb\n--- /dev/null\n+++ b/Documentation/git-check-ignore.txt\n@@ -0,0 +1,58 @@\n+git-check-ignore(1)\n+=================\n+\n+NAME\n+----\n+git-check-ignore - Debug gitignore / exclude files\n+\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git check-ignore' pathname...\n+'git check-ignore' --stdin [-z] < <list-of-paths>\n+\n+DESCRIPTION\n+-----------\n+\n+For each pathname given via the command-line or from a file via\n+`--stdin`, this command will list the first exclude pattern found (if\n+any) which explicitly excludes or includes that pathname.  Note that\n+within any given exclude file, later patterns take precedence over\n+earlier ones, so any matching pattern which this command outputs may\n+not be the one you would immediately expect.\n+\n+OPTIONS\n+-------\n+--stdin::\n+\tRead file names from stdin instead of from the command-line.\n+\n+-z::\n+\tOnly meaningful with `--stdin`; paths are separated with a\n+\tNUL character instead of a linefeed character.\n+\n+OUTPUT\n+------\n+\n+The output is a series of lines of the form:\n+\n+<path> COLON SP <type> SP <pattern> SP <source> SP <position> LF\n+\n+<path> is the path of a file being queried, <type> is either\n+'excluded' or 'included' (for patterns prefixed with '!'), <pattern>\n+is the matching pattern, <source> is the pattern's source file (either\n+as an absolute path or relative to the repository root), and\n+<position> is the position of the pattern within that source.\n+\n+If no pattern matches a given path, nothing will be output for that\n+path.\n+\n+SEE ALSO\n+--------\n+linkgit:gitignore[5]\n+linkgit:gitconfig[5]\n+linkgit:git-ls-files[5]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt\nindex c1f692a..7ba16fe 100644\n--- a/Documentation/gitignore.txt\n+++ b/Documentation/gitignore.txt\n@@ -155,8 +155,10 @@ The second .gitignore prevents git from ignoring\n \n SEE ALSO\n --------\n-linkgit:git-rm[1], linkgit:git-update-index[1],\n-linkgit:gitrepository-layout[5]\n+linkgit:git-rm[1],\n+linkgit:git-update-index[1],\n+linkgit:gitrepository-layout[5],\n+linkgit:git-check-ignore[1]\n \n GIT\n ---\ndiff --git a/Makefile b/Makefile\nindex 272ab69..ce5e42f 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -825,6 +825,7 @@ BUILTIN_OBJS += builtin/branch.o\n BUILTIN_OBJS += builtin/bundle.o\n BUILTIN_OBJS += builtin/cat-file.o\n BUILTIN_OBJS += builtin/check-attr.o\n+BUILTIN_OBJS += builtin/check-ignore.o\n BUILTIN_OBJS += builtin/check-ref-format.o\n BUILTIN_OBJS += builtin/checkout-index.o\n BUILTIN_OBJS += builtin/checkout.o\ndiff --git a/builtin.h b/builtin.h\nindex 8e37752..f812c97 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -57,6 +57,7 @@ extern int cmd_cat_file(int argc, const char **argv, const char *prefix);\n extern int cmd_checkout(int argc, const char **argv, const char *prefix);\n extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);\n extern int cmd_check_attr(int argc, const char **argv, const char *prefix);\n+extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);\n extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/add.c b/builtin/add.c\nindex a7ed2ad..af68c32 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -273,7 +273,7 @@ static int add_files(struct dir_struct *dir, int flags)\n \t\tfprintf(stderr, _(ignore_error));\n \t\tfor (i = 0; i < dir->ignored_nr; i++)\n \t\t\tfprintf(stderr, \"%s\\n\", dir->ignored[i]->name);\n-\t\tfprintf(stderr, _(\"Use -f if you really want to add them.\\n\"));\n+\t\tfprintf(stderr, _(\"Use -f if you really want to add them, or git check-ignore to see\\nwhy they're ignored.\\n\"));\n \t\tdie(_(\"no files added\"));\n \t}\n \ndiff --git a/builtin/check-ignore.c b/builtin/check-ignore.c\nnew file mode 100644\nindex 0000000..146c00b\n--- /dev/null\n+++ b/builtin/check-ignore.c\n@@ -0,0 +1,150 @@\n+#include \"builtin.h\"\n+#include \"cache.h\"\n+#include \"dir.h\"\n+#include \"quote.h\"\n+#include \"pathspec.h\"\n+#include \"parse-options.h\"\n+\n+static int stdin_paths;\n+static const char * const check_ignore_usage[] = {\n+\"git check-ignore pathname...\",\n+\"git check-ignore --stdin [-z] < <list-of-paths>\",\n+NULL\n+};\n+\n+static int null_term_line;\n+\n+static const struct option check_ignore_options[] = {\n+\tOPT_BOOLEAN(0 , \"stdin\", &stdin_paths, \"read file names from stdin\"),\n+\tOPT_BOOLEAN('z', NULL, &null_term_line,\n+\t\t\"input paths are terminated by a null character\"),\n+\tOPT_END()\n+};\n+\n+static void output_exclude(const char *path, struct exclude *exclude)\n+{\n+\tchar *type = exclude->to_exclude ? \"excluded\" : \"included\";\n+\tchar *bang = exclude->to_exclude ? \"\" : \"!\";\n+\tchar *dir  = (exclude->flags & EXC_FLAG_MUSTBEDIR) ? \"/\" : \"\";\n+\tprintf(_(\"%s: %s %s%s%s \"), path, type, bang, exclude->pattern, dir);\n+\tif (exclude->srcpos > 0) {\n+\t\tprintf(\"%s %d\", exclude->src, exclude->srcpos);\n+\t}\n+\telse {\n+\t\t/* Exclude was from CLI parameter.  This code path is\n+\t\t * currently impossible to hit, but later on we might\n+\t\t * want to add ignore tracing to other commands such\n+\t\t * as git clean, which does accept --exclude.\n+\t\t */\n+\t\t/* printf(\"%s %d\", exclude->src, -exclude->srcpos); */\n+\t}\n+\tprintf(\"\\n\");\n+}\n+\n+static void check_ignore(const char *prefix, const char **pathspec)\n+{\n+\tstruct dir_struct dir;\n+\tconst char *path;\n+\tchar *seen = NULL;\n+\n+\t/* read_cache() is only necessary so we can watch out for submodules. */\n+\tif (read_cache() < 0)\n+\t\tdie(_(\"index file corrupt\"));\n+\n+\tmemset(&dir, 0, sizeof(dir));\n+\tdir.flags |= DIR_COLLECT_IGNORED;\n+\tsetup_standard_excludes(&dir);\n+\n+\tif (pathspec) {\n+\t\tint i;\n+\t\tstruct path_exclude_check check;\n+\t\tstruct exclude *exclude;\n+\n+\t\tpath_exclude_check_init(&check, &dir);\n+\t\tif (!seen)\n+\t\t\tseen = find_used_pathspec(pathspec);\n+\t\tfor (i = 0; pathspec[i]; i++) {\n+\t\t\tpath = pathspec[i];\n+\t\t\tchar *full_path =\n+\t\t\t\tprefix_path(prefix, prefix ? strlen(prefix) : 0, path);\n+\t\t\ttreat_gitlink(full_path);\n+\t\t\tvalidate_path(prefix, full_path);\n+\t\t\tif (!seen[i] && path[0]) {\n+\t\t\t\tint dtype = DT_UNKNOWN;\n+\t\t\t\texclude = path_excluded_1(&check, full_path, -1, &dtype);\n+\t\t\t\tif (exclude) {\n+\t\t\t\t\toutput_exclude(path, exclude);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tfree(seen);\n+\t\tfree_directory(&dir);\n+\t\tpath_exclude_check_clear(&check);\n+\t}\n+\telse {\n+\t\tprintf(\"no pathspec\\n\");\n+\t}\n+}\n+\n+static void check_ignore_stdin_paths(const char *prefix)\n+{\n+\tstruct strbuf buf, nbuf;\n+\tchar **pathspec = NULL;\n+\tsize_t nr = 0, alloc = 0;\n+\tint line_termination = null_term_line ? 0 : '\\n';\n+\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_init(&nbuf, 0);\n+\twhile (strbuf_getline(&buf, stdin, line_termination) != EOF) {\n+\t\tif (line_termination && buf.buf[0] == '\"') {\n+\t\t\tstrbuf_reset(&nbuf);\n+\t\t\tif (unquote_c_style(&nbuf, buf.buf, NULL))\n+\t\t\t\tdie(\"line is badly quoted\");\n+\t\t\tstrbuf_swap(&buf, &nbuf);\n+\t\t}\n+\t\tALLOC_GROW(pathspec, nr + 1, alloc);\n+\t\tpathspec[nr] = xcalloc(strlen(buf.buf) + 1, sizeof(*buf.buf));\n+\t\tstrcpy(pathspec[nr++], buf.buf);\n+\t}\n+\tALLOC_GROW(pathspec, nr + 1, alloc);\n+\tpathspec[nr] = NULL;\n+\tcheck_ignore(prefix, (const char **)pathspec);\n+\tmaybe_flush_or_die(stdout, \"attribute to stdout\");\n+\tstrbuf_release(&buf);\n+\tstrbuf_release(&nbuf);\n+\tfree(pathspec);\n+}\n+\n+static NORETURN void error_with_usage(const char *msg)\n+{\n+\terror(\"%s\", msg);\n+\tusage_with_options(check_ignore_usage, check_ignore_options);\n+}\n+\n+int cmd_check_ignore(int argc, const char **argv, const char *prefix)\n+{\n+\tgit_config(git_default_config, NULL);\n+\n+\targc = parse_options(argc, argv, prefix, check_ignore_options,\n+\t\t\t     check_ignore_usage, 0);\n+\n+\tif (stdin_paths) {\n+\t\tif (0 < argc)\n+\t\t\terror_with_usage(\"Can't specify files with --stdin\");\n+\t} else {\n+\t\tif (null_term_line)\n+\t\t\terror_with_usage(\"-z only makes sense with --stdin\");\n+\n+\t\tif (argc == 0)\n+\t\t\terror_with_usage(\"No path specified\");\n+\t}\n+\n+\tif (stdin_paths)\n+\t\tcheck_ignore_stdin_paths(prefix);\n+\telse {\n+\t\tcheck_ignore(prefix, argv);\n+\t\tmaybe_flush_or_die(stdout, \"ignore to stdout\");\n+\t}\n+\n+\treturn 0;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 7e8cfec..bf83303 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -12,6 +12,7 @@ git-branch                              mainporcelain common\n git-bundle                              mainporcelain\n git-cat-file                            plumbinginterrogators\n git-check-attr                          purehelpers\n+git-check-ignore                        purehelpers\n git-checkout                            mainporcelain common\n git-checkout-index                      plumbingmanipulators\n git-check-ref-format                    purehelpers\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 222b804..26d798f 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -594,6 +594,7 @@ __git_list_porcelain_commands ()\n \t\tarchimport)       : import;;\n \t\tcat-file)         : plumbing;;\n \t\tcheck-attr)       : plumbing;;\n+\t\tcheck-ignore)     : plumbing;;\n \t\tcheck-ref-format) : plumbing;;\n \t\tcheckout-index)   : plumbing;;\n \t\tcommit-tree)      : plumbing;;\ndiff --git a/git.c b/git.c\nindex 8788b32..6caf78a 100644\n--- a/git.c\n+++ b/git.c\n@@ -338,6 +338,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"bundle\", cmd_bundle, RUN_SETUP_GENTLY },\n \t\t{ \"cat-file\", cmd_cat_file, RUN_SETUP },\n \t\t{ \"check-attr\", cmd_check_attr, RUN_SETUP },\n+\t\t{ \"check-ignore\", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"check-ref-format\", cmd_check_ref_format },\n \t\t{ \"checkout\", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"checkout-index\", cmd_checkout_index,\ndiff --git a/t/t0007-ignores.sh b/t/t0007-ignores.sh\nnew file mode 100755\nindex 0000000..4407634\n--- /dev/null\n+++ b/t/t0007-ignores.sh\n@@ -0,0 +1,301 @@\n+#!/bin/sh\n+\n+test_description=gitignores\n+\n+. ./test-lib.sh\n+\n+init_vars () {\n+\tglobal_excludes=\"$HOME/global-excludes\"\n+}\n+\n+enable_global_excludes () {\n+\tinit_vars\n+\tgit config core.excludesfile \"$global_excludes\"\n+}\n+\n+ignore_check () {\n+\tpaths=\"$1\" expected=\"$2\" global_args=\"$3\"\n+\n+\tif test -z \"$expected\"; then\n+\t    >\"$HOME/expected\" # avoid newline\n+\telse\n+\t    echo \"$expected\" >\"$HOME/expected\"\n+\tfi &&\n+\trun_check_ignore \"$paths\" \"$global_args\"\n+}\n+\n+expect () {\n+\techo \"$*\" >\"$HOME/expected\"\n+}\n+\n+run_check_ignore () {\n+\targs=\"$1\" global_args=\"$2\"\n+\n+\tinit_vars &&\n+\trm -f \"$HOME/stdout\" \"$HOME/stderr\" \"$HOME/cmd\" &&\n+\techo `which git` $global_args check-ignore $args >\"$HOME/cmd\" &&\n+\tpwd >\"$HOME/pwd\" &&\n+\tgit $global_args check-ignore $args >\"$HOME/stdout\" 2>\"$HOME/stderr\" &&\n+\ttest_cmp \"$HOME/expected\" \"$HOME/stdout\" &&\n+\ttest_line_count = 0 \"$HOME/stderr\"\n+}\n+\n+test_expect_success 'setup' '\n+\tinit_vars\n+\tmkdir -p a/b/ignored-dir a/submodule b &&\n+\tln -s b a/symlink &&\n+\t(\n+\t\tcd a/submodule &&\n+\t\tgit init &&\n+\t\techo a > a &&\n+\t\tgit add a &&\n+\t\tgit commit -m\"commit in submodule\"\n+\t) &&\n+\tgit add a/submodule &&\n+\tcat <<-EOF >.gitignore &&\n+\t\tone\n+\tEOF\n+\tcat <<-EOF >a/.gitignore &&\n+\t\ttwo*\n+\t\t*three\n+\tEOF\n+\tcat <<-EOF >a/b/.gitignore &&\n+\t\tfour\n+\t\tfive\n+\t\t# this comment should affect the line numbers\n+\t\tsix\n+\t\tignored-dir/\n+\t\t# and so should this blank line:\n+\n+\t\t!on*\n+\t\t!two\n+\tEOF\n+\techo \"seven\" >a/b/ignored-dir/.gitignore &&\n+\ttest -n \"$HOME\" &&\n+\tcat <<-EOF >\"$global_excludes\"\n+\t\tglobalone\n+\t\t!globaltwo\n+\t\tglobalthree\n+\tEOF\n+'\n+\n+test_expect_success 'empty command line' '\n+\ttest_must_fail git check-ignore 2>\"$HOME/stderr\" &&\n+\tgrep -q \"error: No path specified\" \"$HOME/stderr\"\n+'\n+\n+test_expect_success 'erroneous use of --' '\n+\ttest_must_fail git check-ignore -- 2>\"$HOME/stderr\" &&\n+\tgrep -q \"error: No path specified\" \"$HOME/stderr\"\n+'\n+\n+test_expect_success '--stdin with superfluous arg' '\n+\ttest_must_fail git check-ignore --stdin foo 2>\"$HOME/stderr\" &&\n+\tgrep -q \"Can'\\''t specify files with --stdin\" \"$HOME/stderr\"\n+'\n+\n+test_expect_success '--stdin -z with superfluous arg' '\n+\ttest_must_fail git check-ignore --stdin -z foo 2>\"$HOME/stderr\" &&\n+\tgrep -q \"Can'\\''t specify files with --stdin\" \"$HOME/stderr\"\n+'\n+\n+test_expect_success '-z without --stdin' '\n+\ttest_must_fail git check-ignore -z 2>\"$HOME/stderr\" &&\n+\tgrep -q \"error: -z only makes sense with --stdin\" \"$HOME/stderr\"\n+'\n+\n+test_expect_success '-z without --stdin and superfluous arg' '\n+\ttest_must_fail git check-ignore -z foo 2>\"$HOME/stderr\" &&\n+\tgrep -q \"error: -z only makes sense with --stdin\" \"$HOME/stderr\"\n+'\n+\n+test_expect_success 'needs work tree' '\n+\t(\n+\t\tcd .git &&\n+\t\ttest_must_fail git check-ignore foo 2>\"$HOME/stderr\"\n+\t) &&\n+\tgrep -q \"fatal: This operation must be run in a work tree\" \"$HOME/stderr\"\n+\n+'\n+test_expect_success 'top-level not ignored' '\n+\tignore_check foo \"\"\n+'\n+\n+test_expect_success 'top-level ignored' '\n+\tignore_check one \"one: excluded one .gitignore 1\"\n+'\n+\n+test_expect_success 'sub-directory ignore from top' '\n+\texpect \"a/one: excluded one .gitignore 1\" &&\n+\trun_check_ignore a/one\n+'\n+\n+test_expect_success 'sub-directory local ignore' '\n+\texpect \"a/3-three: excluded *three a/.gitignore 2\" &&\n+\trun_check_ignore \"a/3-three a/three-not-this-one\"\n+'\n+\n+test_expect_success 'sub-directory local ignore inside a' '\n+\texpect \"3-three: excluded *three a/.gitignore 2\" &&\n+\t(\n+\t\tcd a &&\n+\t\trun_check_ignore \"3-three three-not-this-one\"\n+\t)\n+'\n+\n+test_expect_success 'nested include' '\n+\texpect \"a/b/one: included !on* a/b/.gitignore 8\" &&\n+\trun_check_ignore \"a/b/one\"\n+'\n+\n+test_expect_success 'ignored sub-directory' '\n+\texpect \"a/b/ignored-dir: excluded ignored-dir/ a/b/.gitignore 5\" &&\n+\trun_check_ignore \"a/b/ignored-dir\"\n+'\n+\n+test_expect_success 'multiple files inside ignored sub-directory' '\n+\tcat <<-EOF >\"$HOME/expected\" &&\n+\t\ta/b/ignored-dir/foo: excluded ignored-dir/ a/b/.gitignore 5\n+\t\ta/b/ignored-dir/twoooo: excluded ignored-dir/ a/b/.gitignore 5\n+\t\ta/b/ignored-dir/seven: excluded ignored-dir/ a/b/.gitignore 5\n+\tEOF\n+\trun_check_ignore \"a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven\"\n+'\n+\n+test_expect_success 'cd to ignored sub-directory' '\n+\tcat <<-EOF >\"$HOME/expected\" &&\n+\t\tfoo: excluded ignored-dir/ a/b/.gitignore 5\n+\t\ttwoooo: excluded ignored-dir/ a/b/.gitignore 5\n+\t\t../one: included !on* a/b/.gitignore 8\n+\t\tseven: excluded ignored-dir/ a/b/.gitignore 5\n+\t\t../../one: excluded one .gitignore 1\n+\tEOF\n+\t(\n+\t\tcd a/b/ignored-dir &&\n+\t\trun_check_ignore \"foo twoooo ../one seven ../../one\"\n+\t)\n+'\n+\n+test_expect_success 'symlink' '\n+\tignore_check \"a/symlink\" \"\"\n+'\n+\n+test_expect_success 'beyond a symlink' '\n+\ttest_must_fail git check-ignore \"a/symlink/foo\"\n+'\n+\n+test_expect_success 'beyond a symlink from subdirectory' '\n+\t(\n+\t\tcd a &&\n+\t\ttest_must_fail git check-ignore \"symlink/foo\"\n+\t)\n+'\n+\n+test_expect_success 'submodule' '\n+\ttest_must_fail git check-ignore \"a/submodule/one\" 2>\"$HOME/stderr\" &&\n+\texpect \"fatal: Path '\\''a/submodule/one'\\'' is in submodule '\\''a/submodule'\\''\" &&\n+\ttest_cmp \"$HOME/expected\" \"$HOME/stderr\"\n+'\n+\n+test_expect_success 'submodule from subdirectory' '\n+\t(\n+\t\tcd a &&\n+\t\ttest_must_fail git check-ignore \"submodule/one\" 2>\"$HOME/stderr\"\n+\t) &&\n+\texpect \"fatal: Path '\\''a/submodule/one'\\'' is in submodule '\\''a/submodule'\\''\" &&\n+\ttest_cmp \"$HOME/expected\" \"$HOME/stderr\"\n+'\n+\n+test_expect_success 'global ignore not yet enabled' '\n+\texpect \"a/globalthree: excluded *three a/.gitignore 2\" &&\n+\trun_check_ignore \"globalone a/globalthree a/globaltwo\"\n+'\n+\n+test_expect_success 'global ignore' '\n+\tenable_global_excludes &&\n+\tcat <<-EOF >\"$HOME/expected\" &&\n+\t\tglobalone: excluded globalone $global_excludes 1\n+\t\tglobalthree: excluded globalthree $global_excludes 3\n+\t\ta/globalthree: excluded *three a/.gitignore 2\n+\t\tglobaltwo: included !globaltwo $global_excludes 2\n+\tEOF\n+\trun_check_ignore \"globalone globalthree a/globalthree globaltwo\"\n+'\n+\n+test_expect_success '--stdin' '\n+\tcat <<-EOF >in.txt &&\n+\t\tone\n+\t\ta/one\n+\t\ta/b/on\n+\t\ta/b/one\n+\t\ta/b/two\n+\t\ta/b/twooo\n+\t\tglobaltwo\n+\t\ta/globaltwo\n+\t\ta/b/globaltwo\n+\t\tb/globaltwo\n+\tEOF\n+\tcat <<-EOF >\"$HOME/expected\" &&\n+\t\tone: excluded one .gitignore 1\n+\t\ta/one: excluded one .gitignore 1\n+\t\ta/b/on: included !on* a/b/.gitignore 8\n+\t\ta/b/one: included !on* a/b/.gitignore 8\n+\t\ta/b/two: included !two a/b/.gitignore 9\n+\t\ta/b/twooo: excluded two* a/.gitignore 1\n+\t\tglobaltwo: included !globaltwo $global_excludes 2\n+\t\ta/globaltwo: included !globaltwo $global_excludes 2\n+\t\ta/b/globaltwo: included !globaltwo $global_excludes 2\n+\t\tb/globaltwo: included !globaltwo $global_excludes 2\n+\tEOF\n+\trun_check_ignore --stdin < in.txt\n+'\n+\n+test_expect_success '--stdin -z' '\n+\ttr \"\\n\" \"\\0\" < in.txt | run_check_ignore \"--stdin -z\"\n+'\n+\n+test_expect_success '-z --stdin' '\n+\ttr \"\\n\" \"\\0\" < in.txt | run_check_ignore \"-z --stdin\"\n+'\n+\n+test_expect_success '--stdin from subdirectory' '\n+\tcat <<-EOF >in.txt &&\n+\t\t../one\n+\t\tone\n+\t\tb/on\n+\t\tb/one\n+\t\tb/two\n+\t\tb/twooo\n+\t\t../globaltwo\n+\t\tglobaltwo\n+\t\tb/globaltwo\n+\t\t../b/globaltwo\n+\tEOF\n+\tcat <<-EOF >\"$HOME/expected\" &&\n+\t\t../one: excluded one .gitignore 1\n+\t\tone: excluded one .gitignore 1\n+\t\tb/on: included !on* a/b/.gitignore 8\n+\t\tb/one: included !on* a/b/.gitignore 8\n+\t\tb/two: included !two a/b/.gitignore 9\n+\t\tb/twooo: excluded two* a/.gitignore 1\n+\t\t../globaltwo: included !globaltwo $global_excludes 2\n+\t\tglobaltwo: included !globaltwo $global_excludes 2\n+\t\tb/globaltwo: included !globaltwo $global_excludes 2\n+\t\t../b/globaltwo: included !globaltwo $global_excludes 2\n+\tEOF\n+\t(\n+\t\tcd a &&\n+\t\trun_check_ignore --stdin < ../in.txt\n+\t)\n+'\n+\n+test_expect_success '--stdin -z from subdirectory' '\n+\ttr \"\\n\" \"\\0\" < in.txt | ( cd a && run_check_ignore \"--stdin -z\" )\n+'\n+\n+test_expect_success '-z --stdin from subdirectory' '\n+\ttr \"\\n\" \"\\0\" < in.txt | ( cd a && run_check_ignore \"-z --stdin\" )\n+'\n+\n+\n+test_done\n-- \n1.7.12.155.ge5750d5.dirty\n"},{"id":"198223","messageId":"CACsJy8CLzTddPOCzGhNkOGYjTvEtgWFoD+6DLhAew4Rf1aw5WA@mail.gmail.com","threadId":"31414","inReplyTo":"1346544731-938-10-git-send-email-git@adamspiers.org","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-02T10:41:21Z","receivedAt":"2012-09-02T10:41:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 2, 2012 at 7:12 AM, Adam Spiers <git@adamspiers.org> wrote:\n> This works in a similar manner to git-check-attr.  Some code\n> was reused from add.c by refactoring out into pathspec.c.\n\nThanks, comments from a quick glance. First of all, can we make it\nwork (or share code) with .gitattributes? We may need to debug\n.gitattributes as well as .gitignore. A common command would be nice.\n\n> +SYNOPSIS\n> +--------\n> +[verse]\n> +'git check-ignore' pathname...\n> +'git check-ignore' --stdin [-z] < <list-of-paths>\n\nAlso --quiet option, where check-ignore returns 0 if the given path is\nignored, 1 otherwise?\n\n> +OUTPUT\n> +------\n> +\n> +The output is a series of lines of the form:\n> +\n> +<path> COLON SP <type> SP <pattern> SP <source> SP <position> LF\n> +\n> +<path> is the path of a file being queried, <type> is either\n> +'excluded' or 'included' (for patterns prefixed with '!'), <pattern>\n> +is the matching pattern, <source> is the pattern's source file (either\n> +as an absolute path or relative to the repository root), and\n> +<position> is the position of the pattern within that source.\n\nI think we should have a few levels of verbosity.\n\n - The --quiet I already mention above.\n - If many paths are given, then perhaps we could print ignored paths\n(no extra info).\n - Going to the next level, we could print path and the the location\nof the final exclude/include rule (file and line number).\n - For debugging, given one path, we print all the rules that are\napplied to it, which may help understand how/why it goes wrong.\n\n> @@ -338,6 +338,7 @@ static void handle_internal_command(int argc, const char **argv)\n>                 { \"bundle\", cmd_bundle, RUN_SETUP_GENTLY },\n>                 { \"cat-file\", cmd_cat_file, RUN_SETUP },\n>                 { \"check-attr\", cmd_check_attr, RUN_SETUP },\n> +               { \"check-ignore\", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },\n>                 { \"check-ref-format\", cmd_check_ref_format },\n>                 { \"checkout\", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },\n>                 { \"checkout-index\", cmd_checkout_index,\n\nI don't think we really need NEED_WORK_TREE here. .gitignore can be\nread from index only.\n-- \nDuy\n"},{"id":"198224","messageId":"CAOkDyE9wPUOwJpeKQ5wSCoufqyqE9zwRuBuNvDGEZ-z8452DwA@mail.gmail.com","threadId":"31414","inReplyTo":"CACsJy8CLzTddPOCzGhNkOGYjTvEtgWFoD+6DLhAew4Rf1aw5WA@mail.gmail.com","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-02T14:50:29Z","receivedAt":"2012-09-02T14:50:29Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"Hi there,\n\nFirstly, thanks for the quick feedback!\n\nOn Sun, Sep 2, 2012 at 11:41 AM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n> On Sun, Sep 2, 2012 at 7:12 AM, Adam Spiers <git@adamspiers.org> wrote:\n>> This works in a similar manner to git-check-attr.  Some code\n>> was reused from add.c by refactoring out into pathspec.c.\n>\n> Thanks, comments from a quick glance. First of all, can we make it\n> work (or share code) with .gitattributes? We may need to debug\n> .gitattributes as well as .gitignore. A common command would be nice.\n\nI'm no expert on .gitattributes and check-attr, but AFAICS, all the\nopportunities to share code in the plumbing and front-end seem to be\ntaken already, e.g. the directory traversal and path handling.  The\nCLI argument parsing is necessarily different because check-attr\nrequires a list of attributes as well as a list of files, and of\ncourse the output routines have to be different too.\n\nThe only opportunity for code reuse which I saw but /didn't/ take was\naround the --stdin line parsing code which is duplicated between:\n\n    check_attr_stdin_paths\n    check_ignore_stdin_paths\n    cmd_checkout_index\n    cmd_update_index\n    hash_stdin_paths\n\nI attempted to refactor these, but quickly realised that due to the\nlack of proper closures in C, the overheads and complexity incurred by\nperforming such a refactoring probably outweighed the benefits, so I\ngave up on the idea.\n\nHaving said that, I'm totally open to suggestions if you can spot\nother places where code could be reused :)\n\n>> +SYNOPSIS\n>> +--------\n>> +[verse]\n>> +'git check-ignore' pathname...\n>> +'git check-ignore' --stdin [-z] < <list-of-paths>\n>\n> Also --quiet option, where check-ignore returns 0 if the given path is\n> ignored, 1 otherwise?\n\nI considered that, but couldn't think of appropriate behaviour when\nmultiple paths are given, so in the end I decided to remain consistent\nwith check-attr, which always returns 0.  But I'm happy to change it\nif you can think of a more useful behaviour.  For example we could\nhave a --count option which produces no output but has an exit status\ncorresponding to the number of ignored files.\n\n>  - If many paths are given, then perhaps we could print ignored paths\n> (no extra info).\n\nHow is this different to git ls-files -i -o ?\n\n>  - Going to the next level, we could print path and the the location\n> of the final exclude/include rule (file and line number).\n\nThat's the current behaviour, and I believe it covers the most common\nuse case.\n\n>  - For debugging, given one path, we print all the rules that are\n> applied to it, which may help understand how/why it goes wrong.\n\nThat would be nice, but I'm not sure it's a tremendously common use\ncase.  Could you think of a scenario in which it would be useful?  I\nguess it could be done by adding a new DIR_DEBUG_IGNORED flag to\ndir_struct which would make the exclude matcher functions collect all\nmatching patterns, rather than just returning the first one.  This in\nturn would require another field for collecting all matched patterns.\n\n>> @@ -338,6 +338,7 @@ static void handle_internal_command(int argc, const char **argv)\n>>                 { \"bundle\", cmd_bundle, RUN_SETUP_GENTLY },\n>>                 { \"cat-file\", cmd_cat_file, RUN_SETUP },\n>>                 { \"check-attr\", cmd_check_attr, RUN_SETUP },\n>> +               { \"check-ignore\", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },\n>>                 { \"check-ref-format\", cmd_check_ref_format },\n>>                 { \"checkout\", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },\n>>                 { \"checkout-index\", cmd_checkout_index,\n>\n> I don't think we really need NEED_WORK_TREE here. .gitignore can be\n> read from index only.\n\nI thought about that, but in the end I decided it probably didn't make\nsense, because none of the exclude matching routines match against the\nindex - they all match against the working tree and core.excludesfile.\nThis would also require changing the matching logic to honor the index,\nbut I didn't see the benefit in doing that, since all operations which\ninvolve excludes (add, status, etc.) relate to a work tree.\n\nBut as with all of the above, please don't hesitate to point out if\nI've missed something.  You guys are the experts, not me ;-)\n\nThanks again,\nAdam\n"},{"id":"198225","messageId":"997D30474124498FBFBA89C2E20F7773@PhilipOakley","threadId":"31414","inReplyTo":"1346544731-938-7-git-send-email-git@adamspiers.org","subject":"Re: [PATCH 6/9] For each exclude pattern, store information about where it came from","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":"2012-09-02T17:00:26Z","receivedAt":"2012-09-02T17:00:26Z","isPatch":true,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Adam Spiers\" <git@adamspiers.org>\nSent: Sunday, September 02, 2012 1:12 AM\nSubject: [PATCH 6/9] For each exclude pattern, store information about \nwhere it came from\n\n\n> For exclude patterns read in from files, the filename is stored \n> together\n> with the corresponding line number (counting starting at 1).\n\nIs there a way to identify the config core.excludesfile if present? i.e. \nthat it is from that config variable, rather than directory traversal.\n\nThis came up in a recent \nhttp://stackoverflow.com/questions/12199150/effective-gitignore-and-clean-strategy/12205852#12205852 \ndiscussion.\n\n>\n> For exclude patterns provided on the command line, the sequence number\n> is negative, with counting starting at -1, so for example the 2nd\n> pattern provided via --exclude would be numbered -2.  This allows any\n> future consumers of that data to easily distinguish between exclude\n> patterns from files vs. from the CLI.\n>\n> Signed-off-by: Adam Spiers <git@adamspiers.org>\n> ---\n> builtin/clean.c    |  2 +-\n> builtin/ls-files.c |  3 ++-\n> dir.c              | 25 +++++++++++++++++++------\n> dir.h              |  5 ++++-\n> 4 files changed, 26 insertions(+), 9 deletions(-)\n>\n> diff --git a/builtin/clean.c b/builtin/clean.c\n> index 0c7b3d0..f618231 100644\n> --- a/builtin/clean.c\n> +++ b/builtin/clean.c\n> @@ -99,7 +99,7 @@ int cmd_clean(int argc, const char **argv, const \n> char *prefix)\n>\n>  for (i = 0; i < exclude_list.nr; i++)\n>  add_exclude(exclude_list.items[i].string, \"\", 0,\n> -     &dir.exclude_list[EXC_CMDL]);\n> +     &dir.exclude_list[EXC_CMDL], \"--exclude option\", -(i+1));\n>\n>  pathspec = get_pathspec(prefix, argv);\n>\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index 31b3f2d..420ff40 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -35,6 +35,7 @@ static int error_unmatch;\n> static char *ps_matched;\n> static const char *with_tree;\n> static int exc_given;\n> +static int exclude_args = 0;\n>\n> static const char *tag_cached = \"\";\n> static const char *tag_unmerged = \"\";\n> @@ -423,7 +424,7 @@ static int option_parse_exclude(const struct \n> option *opt,\n>  struct exclude_list *list = opt->value;\n>\n>  exc_given = 1;\n> - add_exclude(arg, \"\", 0, list);\n> + add_exclude(arg, \"\", 0, list, \"--exclude option\", --exclude_args);\n>\n>  return 0;\n> }\n> diff --git a/dir.c b/dir.c\n> index 3d438c3..ac8c838 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -311,7 +311,7 @@ static int no_wildcard(const char *string)\n> }\n>\n> void add_exclude(const char *string, const char *base,\n> - int baselen, struct exclude_list *el)\n> + int baselen, struct exclude_list *el, const char *src, int srcpos)\n> {\n>  struct exclude *x;\n>  size_t len;\n> @@ -341,6 +341,8 @@ void add_exclude(const char *string, const char \n> *base,\n>  x->base = base;\n>  x->baselen = baselen;\n>  x->flags = flags;\n> + x->src = src;\n> + x->srcpos = srcpos;\n>  if (!strchr(string, '/'))\n>  x->flags |= EXC_FLAG_NODIR;\n>  x->nowildcardlen = simple_length(string);\n> @@ -393,7 +395,7 @@ int add_excludes_from_file_to_list(const char \n> *fname,\n>     int check_index)\n> {\n>  struct stat st;\n> - int fd, i;\n> + int fd, i, lineno = 1;\n>  size_t size = 0;\n>  char *buf, *entry;\n>\n> @@ -436,8 +438,9 @@ int add_excludes_from_file_to_list(const char \n> *fname,\n>  if (buf[i] == '\\n') {\n>  if (entry != buf + i && entry[0] != '#') {\n>  buf[i - (i && buf[i-1] == '\\r')] = 0;\n> - add_exclude(entry, base, baselen, el);\n> + add_exclude(entry, base, baselen, el, fname, lineno);\n>  }\n> + lineno++;\n>  entry = buf + i + 1;\n>  }\n>  }\n> @@ -472,8 +475,10 @@ static void prep_exclude(struct dir_struct *dir, \n> const char *base, int baselen)\n>      !strncmp(dir->basebuf, base, stk->baselen))\n>  break;\n>  dir->exclude_stack = stk->prev;\n> - while (stk->exclude_ix < el->nr)\n> - free(el->excludes[--el->nr]);\n> + while (stk->exclude_ix < el->nr) {\n> + struct exclude *exclude = el->excludes[--el->nr];\n> + free(exclude);\n> + }\n>  free(stk->filebuf);\n>  free(stk);\n>  }\n> @@ -500,7 +505,15 @@ static void prep_exclude(struct dir_struct *dir, \n> const char *base, int baselen)\n>  memcpy(dir->basebuf + current, base + current,\n>         stk->baselen - current);\n>  strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);\n> - add_excludes_from_file_to_list(dir->basebuf,\n> +\n> + /* dir->basebuf gets reused by the traversal, but we\n> + * need fname to remain unchanged to ensure the src\n> + * member of each struct exclude correctly back-references\n> + * its source file.\n> + */\n> + char *fname = strdup(dir->basebuf);\n> +\n> + add_excludes_from_file_to_list(fname,\n>         dir->basebuf, stk->baselen,\n>         &stk->filebuf, el, 1);\n>  dir->exclude_stack = stk;\n> diff --git a/dir.h b/dir.h\n> index 1b4f9dc..81efee4 100644\n> --- a/dir.h\n> +++ b/dir.h\n> @@ -31,6 +31,9 @@ struct exclude_list {\n>  int baselen;\n>  int to_exclude;\n>  int flags;\n> + const char *src;\n> + int srcpos; /* counting starts from 1 for line numbers in ignore \n> files,\n> +        and from -1 decrementing for patterns from CLI (--exclude) */\n>  } **excludes;\n> };\n>\n> @@ -123,7 +126,7 @@ extern int add_excludes_from_file_to_list(const \n> char *fname, const char *base, i\n>    char **buf_p, struct exclude_list *el, int check_index);\n> extern void add_excludes_from_file(struct dir_struct *, const char \n> *fname);\n> extern void add_exclude(const char *string, const char *base,\n> - int baselen, struct exclude_list *el);\n> + int baselen, struct exclude_list *el, const char *src, int srcpos);\n> extern void free_excludes(struct exclude_list *el);\n> extern int file_exists(const char *);\n>\n> -- \n> 1.7.12.155.ge5750d5.dirty\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n>\n>\n> -----\n> No virus found in this message.\n> Checked by AVG - www.avg.com\n> Version: 2012.0.2197 / Virus Database: 2437/5240 - Release Date: \n> 09/01/12\n> \n"},{"id":"198228","messageId":"7vipbwgnnv.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"997D30474124498FBFBA89C2E20F7773@PhilipOakley","subject":"Re: [PATCH 6/9] For each exclude pattern, store information about where it came from","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-02T19:02:02Z","receivedAt":"2012-09-02T19:02:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Philip Oakley\" <philipoakley@iee.org> writes:\n\n> Is there a way to identify the config core.excludesfile if present?\n> i.e. that it is from that config variable, rather than directory\n> traversal.\n\nIf the code handles $GIT_DIR/info/exclude then that configuration\nwould also be handled the same way, no?\n"},{"id":"198233","messageId":"7vd324gnnu.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"1346544731-938-4-git-send-email-git@adamspiers.org","subject":"Re: [PATCH 3/9] Rename cryptic 'which' variable to more consistent name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-02T19:56:29Z","receivedAt":"2012-09-02T19:56:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n> 'el' is only *slightly* less cryptic, but is already used as the\n> variable name for a struct exclude_list pointer in numerous other\n> places, so this reduces the number of cryptic variable names in use by\n> one :-)\n\nThe name originally meant to mean \"to which element of the array\ndir_struct.exclude_list[] are we adding this entry?\" but I agree\n\"el\" that stands for ExcludeList would be a better name.\n\nOften we use \"el\" (or \"elem\") for elements of an iterable we are\niterating on in a loop, and the name of the iterable does not have\nto be EsomethingLsomething, by the way.  Because no existing use of\n\"el\" in this file is of that kind, I do not think this change\nintroduces new confusion to the code.\n\n> Signed-off-by: Adam Spiers <git@adamspiers.org>\n> ---\n>  dir.c | 10 +++++-----\n>  dir.h |  4 ++--\n>  2 files changed, 7 insertions(+), 7 deletions(-)\n>\n> diff --git a/dir.c b/dir.c\n> index c9f341a..57a5d11 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -311,7 +311,7 @@ static int no_wildcard(const char *string)\n>  }\n>  \n>  void add_exclude(const char *string, const char *base,\n> -\t\t int baselen, struct exclude_list *which)\n> +\t\t int baselen, struct exclude_list *el)\n>  {\n>  \tstruct exclude *x;\n>  \tsize_t len;\n> @@ -346,8 +346,8 @@ void add_exclude(const char *string, const char *base,\n>  \tx->nowildcardlen = simple_length(string);\n>  \tif (*string == '*' && no_wildcard(string+1))\n>  \t\tx->flags |= EXC_FLAG_ENDSWITH;\n> -\tALLOC_GROW(which->excludes, which->nr + 1, which->alloc);\n> -\twhich->excludes[which->nr++] = x;\n> +\tALLOC_GROW(el->excludes, el->nr + 1, el->alloc);\n> +\tel->excludes[el->nr++] = x;\n>  }\n>  \n>  static void *read_skip_worktree_file_from_index(const char *path, size_t *size)\n> @@ -389,7 +389,7 @@ int add_excludes_from_file_to_list(const char *fname,\n>  \t\t\t\t   const char *base,\n>  \t\t\t\t   int baselen,\n>  \t\t\t\t   char **buf_p,\n> -\t\t\t\t   struct exclude_list *which,\n> +\t\t\t\t   struct exclude_list *el,\n>  \t\t\t\t   int check_index)\n>  {\n>  \tstruct stat st;\n> @@ -436,7 +436,7 @@ int add_excludes_from_file_to_list(const char *fname,\n>  \t\tif (buf[i] == '\\n') {\n>  \t\t\tif (entry != buf + i && entry[0] != '#') {\n>  \t\t\t\tbuf[i - (i && buf[i-1] == '\\r')] = 0;\n> -\t\t\t\tadd_exclude(entry, base, baselen, which);\n> +\t\t\t\tadd_exclude(entry, base, baselen, el);\n>  \t\t\t}\n>  \t\t\tentry = buf + i + 1;\n>  \t\t}\n> diff --git a/dir.h b/dir.h\n> index a226fbc..549a187 100644\n> --- a/dir.h\n> +++ b/dir.h\n> @@ -117,10 +117,10 @@ extern int path_excluded(struct path_exclude_check *, const char *, int namelen,\n>  \n>  \n>  extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,\n> -\t\t\t\t\t  char **buf_p, struct exclude_list *which, int check_index);\n> +\t\t\t\t\t  char **buf_p, struct exclude_list *el, int check_index);\n>  extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n>  extern void add_exclude(const char *string, const char *base,\n> -\t\t\tint baselen, struct exclude_list *which);\n> +\t\t\tint baselen, struct exclude_list *el);\n>  extern void free_excludes(struct exclude_list *el);\n>  extern int file_exists(const char *);\n"},{"id":"198234","messageId":"7v7gscgnnr.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"1346544731-938-1-git-send-email-git@adamspiers.org","subject":"Re: [PATCH 0/9] new git check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-02T20:35:14Z","receivedAt":"2012-09-02T20:35:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n> I was browsing stackoverflow the other day and came across this question:\n>\n>     http://stackoverflow.com/questions/12144633/which-gitignore-rule-is-ignoring-my-file/\n>\n> A quick google revealed this thread from 2009:\n>\n>     http://thread.gmane.org/gmane.comp.version-control.git/108671/focus=108815\n>\n> where Junio and Jeff discussed the possibility of adding a new `git\n> check-ignore' subcommand somewhat analogous to the existing `git\n> check-attr', and suggested the beginnings of an implementation.  It\n> struck me that it might not be too hard to follow these ideas to their\n> natural conclusion, so I decided it would make a fun project :-)\n\nThanks.  I wish there are more people like you ;-)\n\nAs to styles, I spotted only three kinds of \"Huh?\":\n\n * do not initialise statics to 0 or NULL, e.g.\n\n    -static int exclude_args = 0;\n    +static int exclude_args;\n\n * avoid unnnecessary braces {} around single statement blocks, e.g.\n\n    -if (exclude) {\n    +if (exclude)\n        return exclude;\n    -}\n\n * else should follow close brace '}' of if clause, e.g.\n\n     if (...) {\n         ...\n    -}\n    -else {\n    +} else {\n         ...\n\nFor reviews on substance, please see other messages from me.\n"},{"id":"198230","messageId":"7v1uikgnnp.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"CAOkDyE9wPUOwJpeKQ5wSCoufqyqE9zwRuBuNvDGEZ-z8452DwA@mail.gmail.com","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-02T20:38:41Z","receivedAt":"2012-09-02T20:38:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n>>> +SYNOPSIS\n>>> +--------\n>>> +[verse]\n>>> +'git check-ignore' pathname...\n>>> +'git check-ignore' --stdin [-z] < <list-of-paths>\n>>\n>> Also --quiet option, where check-ignore returns 0 if the given path is\n>> ignored, 1 otherwise?\n\nI agree that multiple paths are problematic.\n\nWe could error out if multiple paths are given with --quiet until we\nfigure out what the useful result would be in such a case, and still\ngive a useful answer to callers that feed a single path, though.\n\nThat may encourage suboptimal coding to casual Porcelain writers,\ni.e. it would allow\n\n\tfor path in $paths\n        do\n\t\tif git check-ignore -q \"$path\"\n                then\n\t\t\tdo something to \"$path\"\n\t\tfi\n\tdone\n\neven though we would rather want to encourage\n\n\tgit check-ignore --name-only $paths |\n        while read path\n        do\n\t\tdo something to \"$path\"\n\tdone\n\nBut from lay-scriptors' point of view, being able to easily write a\nscript (even though it may be inefficient) to do the job at hand is\nfar better than having to give up writing one because the tool does\nnot allow easy-and-stupid scripting, so it is not exactly a huge\ndownside.\n\n>>  - If many paths are given, then perhaps we could print ignored paths\n>> (no extra info).\n>\n> How is this different to git ls-files -i -o ?\n\nI personally think the parts of ls-files that deal with paths not in\nthe index outlived its usefulness ;-) and users deserve to be given\na better UI.\n\n>>  - Going to the next level, we could print path and the the location\n>> of the final exclude/include rule (file and line number).\n>\n> That's the current behaviour, and I believe it covers the most common\n> use case.\n\nYes; I have a reservation on your output format, though.\n\n>>  - For debugging, given one path, we print all the rules that are\n>> applied to it, which may help understand how/why it goes wrong.\n\nI do not think that would be terribly useful.  Maybe for people who\nare learning how dir.c internally works, but not for people who are\ntrying to improve the set of .gitignore files in their project.\n\n> I thought about that, but in the end I decided it probably didn't make\n> sense, because none of the exclude matching routines match against the\n> index - they all match against the working tree and core.excludesfile.\n> This would also require changing the matching logic to honor the index,\n> but I didn't see the benefit in doing that, since all operations which\n> involve excludes (add, status, etc.) relate to a work tree.\n\nThe mechanism primarily is to see if a path in the working tree is a\ncruft or a valuable still to be added; I am OK with NEED_WORK_TREE;\nwhen we have a useful case to run this in a bare repository, we can\nlift it.\n\nAs with the \"what to do with multiple paths and -q\", it is better to\nstart with feature set to cover only the known or easily anticipated\nuse cases, rejecting the cases for which good semantics are not\nthought out.\n\nAn alternative would be a code that operates sanely only for known\nor anticipated cases and do random things with irrational semantics\nin other cases, and people start relying on the irrational behaviour\nwithout realizing their input and the behaviour they are seeing are\nnot something that the feature is designed to for, but whatever the\ncode with loose precondition checking happens to do.  We do not want\nto repeat that kind of mistake, which is hard to fix in future\nversions.\n"},{"id":"198232","messageId":"7vvcfwf937.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"1346544731-938-10-git-send-email-git@adamspiers.org","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-02T20:41:24Z","receivedAt":"2012-09-02T20:41:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n> +OPTIONS\n> +-------\n> +--stdin::\n> +\tRead file names from stdin instead of from the command-line.\n> +\n> +-z::\n> +\tOnly meaningful with `--stdin`; paths are separated with a\n> +\tNUL character instead of a linefeed character.\n\nOn input, or on output, or both?\n\nThe answer should be \"both\", otherwise you cannot safely handle\npaths with funny character in your script, even if you wanted to.\nWhich means that this cannot only be meaningful with \"--stdin\", I\nthink.\n\n> +OUTPUT\n> +------\n> +\n> +The output is a series of lines of the form:\n> +\n> +<path> COLON SP <type> SP <pattern> SP <source> SP <position> LF\n> +\n> +<path> is the path of a file being queried, <type> is either\n> +'excluded' or 'included' (for patterns prefixed with '!'), <pattern>\n> +is the matching pattern, <source> is the pattern's source file (either\n> +as an absolute path or relative to the repository root), and\n> +<position> is the position of the pattern within that source.\n\nLet's step back a bit and think what this command is about.  What is\nthe reason why the user wants to run \"check-ignore $path\" in the\nfirst place?  I think there are two (or three, depending on how you\ncount).\n\n (1) You have one (or more) paths at hand.  You want to know if it\n     is (or some of them are) ignored, but you do not particularly\n     care how they are ignored.  Think of implementing your own \"git\n     add\" as a script.\n\n (2) You have one or more paths that are ignored but do not want\n     them to be, and want to find out why they are.  \n\nFor the former, your script may want to see the paths sifted into\n\"ignored\" bin and \"not-ignored\" bin, so\n\n\tgit check-ignore [-z] --name-only $paths\n\nthat gives you only the paths without any reason is more useful.\nYou also may want the opposite (show only paths not ignored), but\nthat can be computed easily by the script, so it is of lessor\nimportance.\n\nFor the latter, you are debugging the set of exclude sources and\nwant to learn where the decision to exclude it comes from.  For that\nkind of use, it would be more useful if the output mimicked error\nmessages from the compilers and output from \"grep -n\" to show the\nsource, e.g.\n\n\t.gitignore:13:/git-am\tgit-am\n\nEmacs users can use \"M-x grep<RET>git check-ignore -v git-am<RET>\",\nsee the output, and find the hit in its output (I would imagine vim\nwould have a similar feature).  The output format would be something\nlike:\n\n\t<source> <COLON> <linenum> <COLON> <pattern> <HT> <pathname>\n\nI do not think you need excluded/included <type> as a separate item\nin the non \"-z\" output, as it should be clear from the <pattern>.\nSubstitute \"<cmdline>\" (literally) as source for patterns obtained\nfrom the command line.\n\nI would also suggest to\n\n (1) make --name-only the default (i.e. no need to have the\n     \"--name-only\" option);\n\n (2) give the version that mimicks \"grep -n\" when \"-v|--verbose\" is\n     given; and\n\n (3) support \"--quiet\"; the command would exit with status 0 if\n     _any_ of the paths given is ignored, or status 1 if none of the\n     paths is ignored (or error out with die() when --quiet and\n     multiple paths are given).\n\nRegarding \"-z\" (for script consumption), I do not object to the\nbroken down format, e.g., \"check-ignore -z -v\" may give a sequence\nof\n\n    <pathname> NUL <type> NUL <pattern> NUL <source> NUL <position> NUL\n\nwhile \"check-ignore -z\" would give a sequence of\n\n    <pathname> NUL\n"},{"id":"198236","messageId":"A05E4811A66D499DA6AC932AC49F1E8F@PhilipOakley","threadId":"31414","inReplyTo":"7vipbwgnnv.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 6/9] For each exclude pattern, store information about where it came from","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":"2012-09-02T22:36:59Z","receivedAt":"2012-09-02T22:36:59Z","isPatch":true,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Junio C Hamano\" <gitster@pobox.com>\nSent: Sunday, September 02, 2012 8:02 PM\n> \"Philip Oakley\" <philipoakley@iee.org> writes:\n>\n>> Is there a way to identify the config core.excludesfile if present?\n>> i.e. that it is from that config variable, rather than directory\n>> traversal.\n>\n> If the code handles $GIT_DIR/info/exclude then that configuration\n> would also be handled the same way, no?\n\nProbably not. The $GIT_DIR/info/exclude is directly a path, while the \ncore.excludesfile could point anywhere. This assumes the path to the \nrelevant ignore file is shown.\n\nGiven the suggested report format in the Documentation, this path could \nbe reported as 'coreexclude', not just an 'exclude'.\n\nIf I've understood the regular code correctly, the core.excludesfile is \nalways at one end of the exclude struct so should be easy to check at \nthat position. \n"},{"id":"198238","messageId":"7vipbvg9ni.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"7vvcfwf937.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-03T01:47:13Z","receivedAt":"2012-09-03T01:47:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Let's step back a bit and think what this command is about.  What is\n> the reason why the user wants to run \"check-ignore $path\" in the\n> first place?  I think there are two (or three, depending on how you\n> count).\n>\n>  (1) You have one (or more) paths at hand.  You want to know if it\n>      is (or some of them are) ignored, but you do not particularly\n>      care how they are ignored.  Think of implementing your own \"git\n>      add\" as a script.\n>\n>  (2) You have one or more paths that are ignored but do not want\n>      them to be, and want to find out why they are.  \n\nA reason related to (2) is to find out why the paths you want to be\nexcluded are included, so that you can fix it by disabling an entry\nin .gitignore that covers too widely, or by adding a new entry to\noverride it.\n\nFor that to work, the \"-v\" mode needs to show all paths that were\ngiven from the command line (or --stdin), to explain why each of\nthem is ignored or not ignored.  Hence, in addition:\n\n> For the latter, you are debugging the set of exclude sources and\n> want to learn where the decision to exclude it comes from.  For that\n> kind of use, it would be more useful if the output mimicked error\n> messages from the compilers and output from \"grep -n\" to show the\n> source, e.g.\n>\n> \t.gitignore:13:/git-am\tgit-am\n\nwe would need an entry that shows !<include pattern> in the output.\n\nA path that does not match any pattern anywhere in the exclude files\nis taken as included---I am not sure what is the best way to explain\nthe reason why they are included.  If we are going to show the entry\nthat finally matched (either with negative or positive pattern) and\ndecided the \"fate\" as the explanation for both excluded and included\npaths, perhaps not showing an entry for such a \"never matched\" path\nmight be a good enough explanation.\n"},{"id":"198240","messageId":"CACsJy8BO9pOEK4GykTAR94TF7ff3Bkx4db8ifcYVP32FNigmRA@mail.gmail.com","threadId":"31414","inReplyTo":"CAOkDyE9wPUOwJpeKQ5wSCoufqyqE9zwRuBuNvDGEZ-z8452DwA@mail.gmail.com","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-03T04:14:22Z","receivedAt":"2012-09-03T04:14:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 2, 2012 at 9:50 PM, Adam Spiers <git@adamspiers.org> wrote:\n> I'm no expert on .gitattributes and check-attr, but AFAICS, all the\n> opportunities to share code in the plumbing and front-end seem to be\n> taken already, e.g. the directory traversal and path handling.  The\n> CLI argument parsing is necessarily different because check-attr\n> requires a list of attributes as well as a list of files, and of\n> course the output routines have to be different too.\n>\n> The only opportunity for code reuse which I saw but /didn't/ take was\n> around the --stdin line parsing code which is duplicated between:\n>\n>     check_attr_stdin_paths\n>     check_ignore_stdin_paths\n>     cmd_checkout_index\n>     cmd_update_index\n>     hash_stdin_paths\n>\n> I attempted to refactor these, but quickly realised that due to the\n> lack of proper closures in C, the overheads and complexity incurred by\n> performing such a refactoring probably outweighed the benefits, so I\n> gave up on the idea.\n>\n> Having said that, I'm totally open to suggestions if you can spot\n> other places where code could be reused :)\n\nYeah. That was my impression too. I just hoped a new set of eyes might\ndiscover something ;) At lease we could prepare the output format that\ncan be reused (maybe with little changes) for check-attr debugging if\nit comes later. Or make this command part of check-attr..\n\n>> Also --quiet option, where check-ignore returns 0 if the given path is\n>> ignored, 1 otherwise?\n>\n> I considered that, but couldn't think of appropriate behaviour when\n> multiple paths are given, so in the end I decided to remain consistent\n> with check-attr, which always returns 0.  But I'm happy to change it\n> if you can think of a more useful behaviour.  For example we could\n> have a --count option which produces no output but has an exit status\n> corresponding to the number of ignored files.\n\nWe could take this opportunity to kill \"add --ignore-missing\", which\nis basically .gitignore checking and it accepts multiple paths, I\nthink.\n\n>>  - If many paths are given, then perhaps we could print ignored paths\n>> (no extra info).\n>\n> How is this different to git ls-files -i -o ?\n\nI think ls-files requires real files on working directory, but\ncheck-ignore can deal with just non-existing paths.\n\n>>  - For debugging, given one path, we print all the rules that are\n>> applied to it, which may help understand how/why it goes wrong.\n>\n> That would be nice, but I'm not sure it's a tremendously common use\n> case.  Could you think of a scenario in which it would be useful?  I\n> guess it could be done by adding a new DIR_DEBUG_IGNORED flag to\n> dir_struct which would make the exclude matcher functions collect all\n> matching patterns, rather than just returning the first one.  This in\n> turn would require another field for collecting all matched patterns.\n\nMixing include/exclude ignore rules multiple times could be hard to\nfigure out what goes wrong. But as we haven't seen an actual use case\nyet, just leave it out.\n\n>> I don't think we really need NEED_WORK_TREE here. .gitignore can be\n>> read from index only.\n>\n> I thought about that, but in the end I decided it probably didn't make\n> sense, because none of the exclude matching routines match against the\n> index - they all match against the working tree and core.excludesfile.\n> This would also require changing the matching logic to honor the index,\n> but I didn't see the benefit in doing that, since all operations which\n> involve excludes (add, status, etc.) relate to a work tree.\n>\n> But as with all of the above, please don't hesitate to point out if\n> I've missed something.  You guys are the experts, not me ;-)\n\nAgain I was thinking that check-ignore could work with imaginary paths\nand could be used by scripts. If a script just wants to check certain\npaths are excluded, it should not need to move to working directory\n(though it probably is in working directory most of the time).\n-- \nDuy\n"},{"id":"198299","messageId":"CACsJy8AJoR-b6bbKraNp+-eYE8vGUMUTJ2siH=rdUv2POEe-0Q@mail.gmail.com","threadId":"31414","inReplyTo":"1346544731-938-5-git-send-email-git@adamspiers.org","subject":"Re: [PATCH 4/9] Refactor excluded_from_list","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-04T12:32:09Z","receivedAt":"2012-09-04T12:32:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 2, 2012 at 7:12 AM, Adam Spiers <git@adamspiers.org> wrote:\n> diff --git a/dir.c b/dir.c\n> index 57a5d11..3a532d5 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -509,22 +509,24 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n>         dir->basebuf[baselen] = '\\0';\n>  }\n>\n> -/* Scan the list and let the last match determine the fate.\n> - * Return 1 for exclude, 0 for include and -1 for undecided.\n> +/*\n> + * Scan the given exclude list in reverse to see whether pathname\n> + * should be ignored.  The first match (i.e. the last on the list), if\n> + * any, determines the fate.  Returns the exclude_list element which\n> + * matched, or NULL for undecided.\n>   */\n> -int excluded_from_list(const char *pathname,\n> -                      int pathlen, const char *basename, int *dtype,\n> -                      struct exclude_list *el)\n> +struct exclude *excluded_from_list_1(const char *pathname, int pathlen,\n> +                                    const char *basename, int *dtype,\n> +                                    struct exclude_list *el)\n>  {\n>         int i;\n>\n\nYou should keep the new function static.\n-- \nDuy\n"},{"id":"198300","messageId":"CACsJy8A-P0RziZt1_PajFrzqmq9ZbkyaxwUCeDAO3XteQ0gAag@mail.gmail.com","threadId":"31414","inReplyTo":"1346544731-938-6-git-send-email-git@adamspiers.org","subject":"Re: [PATCH 5/9] Refactor excluded and path_excluded","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-04T12:40:33Z","receivedAt":"2012-09-04T12:40:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 2, 2012 at 7:12 AM, Adam Spiers <git@adamspiers.org> wrote:\n>  extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *);\n>  extern void path_exclude_check_clear(struct path_exclude_check *);\n> +extern struct exclude *path_excluded_1(struct path_exclude_check *, const char *,\n> +                                      int namelen, int *dtype);\n>  extern int path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);\n\nExported functions should have nicer names than *_1. No idea what are\nbetter names though, maybe exclude_path?\n-- \nDuy\n"},{"id":"198301","messageId":"CACsJy8A2-C9xSz2LXt9Ptjxhe++i2vcBSMY-cxJLWUiutajZUQ@mail.gmail.com","threadId":"31414","inReplyTo":"1346544731-938-10-git-send-email-git@adamspiers.org","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-04T13:06:12Z","receivedAt":"2012-09-04T13:06:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 2, 2012 at 7:12 AM, Adam Spiers <git@adamspiers.org> wrote:\n> --- a/builtin/add.c\n> +++ b/builtin/add.c\n> @@ -273,7 +273,7 @@ static int add_files(struct dir_struct *dir, int flags)\n>                 fprintf(stderr, _(ignore_error));\n>                 for (i = 0; i < dir->ignored_nr; i++)\n>                         fprintf(stderr, \"%s\\n\", dir->ignored[i]->name);\n> -               fprintf(stderr, _(\"Use -f if you really want to add them.\\n\"));\n> +               fprintf(stderr, _(\"Use -f if you really want to add them, or git check-ignore to see\\nwhy they're ignored.\\n\"));\n>                 die(_(\"no files added\"));\n>         }\n\nString too long (> 80 chars).\n\n> +static const char * const check_ignore_usage[] = {\n> +\"git check-ignore pathname...\",\n> +\"git check-ignore --stdin [-z] < <list-of-paths>\",\n> +NULL\n> +};\n> +\n> +static const struct option check_ignore_options[] = {\n> +       OPT_BOOLEAN(0 , \"stdin\", &stdin_paths, \"read file names from stdin\"),\n> +       OPT_BOOLEAN('z', NULL, &null_term_line,\n> +               \"input paths are terminated by a null character\"),\n> +       OPT_END()\n> +};\n\nYou may want to mark help strings (\"read file names from stdin\" and\n\"input paths... null character\") and check_ignore_usage[] for\ntranslation. Just wrap those strings with N_() and you'll be fine. For\nsimilar changes, check out nd/i18n-parseopt-help on branch 'pu'.\n\n> +static void output_exclude(const char *path, struct exclude *exclude)\n> +{\n> +       char *type = exclude->to_exclude ? \"excluded\" : \"included\";\n> +       char *bang = exclude->to_exclude ? \"\" : \"!\";\n> +       char *dir  = (exclude->flags & EXC_FLAG_MUSTBEDIR) ? \"/\" : \"\";\n> +       printf(_(\"%s: %s %s%s%s \"), path, type, bang, exclude->pattern, dir);\n\nThese English words \"excluded\" and \"included\" make the translator me\nwant to translate them. But they could be the markers for scripts, so\nthey may not be translated. How about using non alphanumeric letters\ninstead?\n\n> +static void check_ignore(const char *prefix, const char **pathspec)\n> +{\n> +       struct dir_struct dir;\n> +       const char *path;\n> +       char *seen = NULL;\n> +\n> +       /* read_cache() is only necessary so we can watch out for submodules. */\n> +       if (read_cache() < 0)\n> +               die(_(\"index file corrupt\"));\n> +\n> +       memset(&dir, 0, sizeof(dir));\n> +       dir.flags |= DIR_COLLECT_IGNORED;\n> +       setup_standard_excludes(&dir);\n\nYou should support ignore rules from files and command line arguments\ntoo, like ls-files. For quick testing.\n\n> +static NORETURN void error_with_usage(const char *msg)\n> +{\n> +       error(\"%s\", msg);\n> +       usage_with_options(check_ignore_usage, check_ignore_options);\n> +}\n\nInteresting. We have usage_msg_opt() in parse-options.c, but it's more\nverbose. Perhaps this function should be moved to parse-options.c\nbecause it may be useful to other commands as well?\n-- \nDuy\n"},{"id":"198312","messageId":"7vr4qhbt3d.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"CACsJy8A-P0RziZt1_PajFrzqmq9ZbkyaxwUCeDAO3XteQ0gAag@mail.gmail.com","subject":"Re: [PATCH 5/9] Refactor excluded and path_excluded","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-04T17:23:02Z","receivedAt":"2012-09-04T17:23:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> On Sun, Sep 2, 2012 at 7:12 AM, Adam Spiers <git@adamspiers.org> wrote:\n>>  extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *);\n>>  extern void path_exclude_check_clear(struct path_exclude_check *);\n>> +extern struct exclude *path_excluded_1(struct path_exclude_check *, const char *,\n>> +                                      int namelen, int *dtype);\n>>  extern int path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);\n>\n> Exported functions should have nicer names than *_1. No idea what are\n> better names though, maybe exclude_path?\n\nWhich part is better?\n\nJust like between path_excluded_1() and path_excluded() nobody can\ntell how they differ (except perhaps the former smells more special\npurpose thanks to its funny name) and wouldn't be able to tell which\none to call without looking at their sources, it is hard to tell\npath_excluded() and exclude_path() apart.  In a sense, that pair is\neven worse as there is no hint to suggest which one is more exotic\nbetween them in their names.\n"},{"id":"198315","messageId":"7vmx15bsxj.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"CACsJy8A2-C9xSz2LXt9Ptjxhe++i2vcBSMY-cxJLWUiutajZUQ@mail.gmail.com","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-04T17:26:32Z","receivedAt":"2012-09-04T17:26:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n>> +static void output_exclude(const char *path, struct exclude *exclude)\n>> +{\n>> +       char *type = exclude->to_exclude ? \"excluded\" : \"included\";\n>> +       char *bang = exclude->to_exclude ? \"\" : \"!\";\n>> +       char *dir  = (exclude->flags & EXC_FLAG_MUSTBEDIR) ? \"/\" : \"\";\n>> +       printf(_(\"%s: %s %s%s%s \"), path, type, bang, exclude->pattern, dir);\n>\n> These English words \"excluded\" and \"included\" make the translator me\n> want to translate them. But they could be the markers for scripts, so\n> they may not be translated. How about using non alphanumeric letters\n> instead?\n\nI agree they should not be translated, but it is a disease to think\nunintelligible mnemonic is a better input format for scripts than\nthe spelled out words.  \"excluded/included\" pair is just fine.\n"},{"id":"198366","messageId":"CACsJy8CPaJKGU0Lyjr93s9JEc_VBuLrHNH=nRa42K8Ohj3OWFA@mail.gmail.com","threadId":"31414","inReplyTo":"7vmx15bsxj.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-05T10:25:25Z","receivedAt":"2012-09-05T10:25:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Sep 5, 2012 at 12:26 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>>> +static void output_exclude(const char *path, struct exclude *exclude)\n>>> +{\n>>> +       char *type = exclude->to_exclude ? \"excluded\" : \"included\";\n>>> +       char *bang = exclude->to_exclude ? \"\" : \"!\";\n>>> +       char *dir  = (exclude->flags & EXC_FLAG_MUSTBEDIR) ? \"/\" : \"\";\n>>> +       printf(_(\"%s: %s %s%s%s \"), path, type, bang, exclude->pattern, dir);\n>>\n>> These English words \"excluded\" and \"included\" make the translator me\n>> want to translate them. But they could be the markers for scripts, so\n>> they may not be translated. How about using non alphanumeric letters\n>> instead?\n>\n> I agree they should not be translated, but it is a disease to think\n> unintelligible mnemonic is a better input format for scripts than\n> the spelled out words.  \"excluded/included\" pair is just fine.\n\nNot all mnemonic is unintelligible though. \"+\" and \"-\" may fit well in\nthis case. I'm just trying to make sure we have checked the mnemonic\npool before ending up with excluded/included.\n-- \nDuy\n"},{"id":"198367","messageId":"CACsJy8D23tDa3SJO6yegHFs2hT+bTr6mLTn16ZU3kiT1dtj4vw@mail.gmail.com","threadId":"31414","inReplyTo":"7vr4qhbt3d.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 5/9] Refactor excluded and path_excluded","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-05T10:28:39Z","receivedAt":"2012-09-05T10:28:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Sep 5, 2012 at 12:23 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> On Sun, Sep 2, 2012 at 7:12 AM, Adam Spiers <git@adamspiers.org> wrote:\n>>>  extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *);\n>>>  extern void path_exclude_check_clear(struct path_exclude_check *);\n>>> +extern struct exclude *path_excluded_1(struct path_exclude_check *, const char *,\n>>> +                                      int namelen, int *dtype);\n>>>  extern int path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);\n>>\n>> Exported functions should have nicer names than *_1. No idea what are\n>> better names though, maybe exclude_path?\n>\n> Which part is better?\n>\n> Just like between path_excluded_1() and path_excluded() nobody can\n> tell how they differ (except perhaps the former smells more special\n> purpose thanks to its funny name) and wouldn't be able to tell which\n> one to call without looking at their sources, it is hard to tell\n> path_excluded() and exclude_path() apart.  In a sense, that pair is\n> even worse as there is no hint to suggest which one is more exotic\n> between them in their names.\n\nWe could introduce exclude_path() and kill path_excluded() then. There\nare just about 5-6 call sites to replace.\n-- \nDuy\n"},{"id":"198411","messageId":"7v627r7s5c.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"CACsJy8D23tDa3SJO6yegHFs2hT+bTr6mLTn16ZU3kiT1dtj4vw@mail.gmail.com","subject":"Re: [PATCH 5/9] Refactor excluded and path_excluded","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-06T03:21:35Z","receivedAt":"2012-09-06T03:21:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> We could introduce exclude_path() and kill path_excluded() then. There\n> are just about 5-6 call sites to replace.\n\nThe name path_excluded(... path ...) sounds like it is asking a\nyes/no question \"is this path excluded?\", which actually is what is\ngoing on.\n\nThe name exclude_path(... path ...) sounds as if you are requesting\nsomebody to exclude the path.  Does that meaning match the semantics\nof the function?\n"},{"id":"198434","messageId":"CACsJy8Aq7Sodm1_k2kAmfajHG4wP76xHCshDGPfiLYfzuNwWaA@mail.gmail.com","threadId":"31414","inReplyTo":"7v627r7s5c.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 5/9] Refactor excluded and path_excluded","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-06T12:13:37Z","receivedAt":"2012-09-06T12:13:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Sep 6, 2012 at 10:21 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> We could introduce exclude_path() and kill path_excluded() then. There\n>> are just about 5-6 call sites to replace.\n>\n> The name path_excluded(... path ...) sounds like it is asking a\n> yes/no question \"is this path excluded?\", which actually is what is\n> going on.\n>\n> The name exclude_path(... path ...) sounds as if you are requesting\n> somebody to exclude the path.  Does that meaning match the semantics\n> of the function?\n\nI'm not great at naming. And path_excluded() cannot be reused to avoid\nproblems with other ongoing series if any. So path_is_excluded()?\n-- \nDuy\n"},{"id":"198450","messageId":"CACnwZYe19r9mefDmAQtuE5NU4jw033fc_i4JvMZUgtMUkNgEOw@mail.gmail.com","threadId":"31414","inReplyTo":"CACsJy8Aq7Sodm1_k2kAmfajHG4wP76xHCshDGPfiLYfzuNwWaA@mail.gmail.com","subject":"Re: [PATCH 5/9] Refactor excluded and path_excluded","fromName":"Thiago Farina","fromEmail":"tfransosi@gmail.com","sentAt":"2012-09-06T14:59:15Z","receivedAt":"2012-09-06T14:59:15Z","isPatch":true,"sender":{"key":"tfransosi@gmail.com","avatar":"https://avatars.githubusercontent.com/u/970071?v=4"},"body":"On Thu, Sep 6, 2012 at 9:13 AM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n> On Thu, Sep 6, 2012 at 10:21 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>>\n>>> We could introduce exclude_path() and kill path_excluded() then. There\n>>> are just about 5-6 call sites to replace.\n>>\n>> The name path_excluded(... path ...) sounds like it is asking a\n>> yes/no question \"is this path excluded?\", which actually is what is\n>> going on.\n>>\n>> The name exclude_path(... path ...) sounds as if you are requesting\n>> somebody to exclude the path.  Does that meaning match the semantics\n>> of the function?\n>\n> I'm not great at naming. And path_excluded() cannot be reused to avoid\n> problems with other ongoing series if any. So path_is_excluded()?\n\nis_path_excluded()?\n"},{"id":"198451","messageId":"CACsJy8CoBvxg9SWFFXbuDJM7Lu74Jw6LVc=7cNwu24h7pijeXQ@mail.gmail.com","threadId":"31414","inReplyTo":"CACnwZYe19r9mefDmAQtuE5NU4jw033fc_i4JvMZUgtMUkNgEOw@mail.gmail.com","subject":"Re: [PATCH 5/9] Refactor excluded and path_excluded","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-06T15:05:45Z","receivedAt":"2012-09-06T15:05:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Sep 6, 2012 at 9:59 PM, Thiago Farina <tfransosi@gmail.com> wrote:\n> On Thu, Sep 6, 2012 at 9:13 AM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n>> On Thu, Sep 6, 2012 at 10:21 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>>>\n>>>> We could introduce exclude_path() and kill path_excluded() then. There\n>>>> are just about 5-6 call sites to replace.\n>>>\n>>> The name path_excluded(... path ...) sounds like it is asking a\n>>> yes/no question \"is this path excluded?\", which actually is what is\n>>> going on.\n>>>\n>>> The name exclude_path(... path ...) sounds as if you are requesting\n>>> somebody to exclude the path.  Does that meaning match the semantics\n>>> of the function?\n>>\n>> I'm not great at naming. And path_excluded() cannot be reused to avoid\n>> problems with other ongoing series if any. So path_is_excluded()?\n>\n> is_path_excluded()?\n\nGood too.\n-- \nDuy\n"},{"id":"198465","messageId":"CAOkDyE8pLO3MKVhtW5WEAmqMROV-QKrMOJHN7JKvwYNy7VgFiQ@mail.gmail.com","threadId":"31414","inReplyTo":"CACsJy8CoBvxg9SWFFXbuDJM7Lu74Jw6LVc=7cNwu24h7pijeXQ@mail.gmail.com","subject":"Re: [PATCH 5/9] Refactor excluded and path_excluded","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-06T17:42:31Z","receivedAt":"2012-09-06T17:42:31Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Thu, Sep 6, 2012 at 4:05 PM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n> On Thu, Sep 6, 2012 at 9:59 PM, Thiago Farina <tfransosi@gmail.com> wrote:\n>> On Thu, Sep 6, 2012 at 9:13 AM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n>>> On Thu, Sep 6, 2012 at 10:21 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>>> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>>>>\n>>>>> We could introduce exclude_path() and kill path_excluded() then. There\n>>>>> are just about 5-6 call sites to replace.\n>>>>\n>>>> The name path_excluded(... path ...) sounds like it is asking a\n>>>> yes/no question \"is this path excluded?\", which actually is what is\n>>>> going on.\n>>>>\n>>>> The name exclude_path(... path ...) sounds as if you are requesting\n>>>> somebody to exclude the path.  Does that meaning match the semantics\n>>>> of the function?\n>>>\n>>> I'm not great at naming. And path_excluded() cannot be reused to avoid\n>>> problems with other ongoing series if any. So path_is_excluded()?\n>>\n>> is_path_excluded()?\n\nIt makes me happy to see this suggestion :-) Because this is the exact\nname I changed it to in an earlier version of my patch series.  But\nthen I got worried that a) it did not fit the `foo_1' convention which\nalready seemed to be in use, and b) people would complain about API\nchanges.  So I changed it back.  However if you would prefer to rename\nthe functions for clarity, then I have more suggestions:\n\n  old                       new\n  -----------------------------------------------------------\n  path_excluded             is_path_excluded\n  excluded                  is_excluded\n  excluded_from_list        is_excluded_from_list\n  path_excluded_1           last_exclude_matching_path\n  excluded_1                last_exclude_matching\n  excluded_from_list_1      last_exclude_matching_from_list\n"},{"id":"198466","messageId":"CAOkDyE-691hi8Qx8eOKyU8=fy=GqA+NXu-41gXAmX5NViG9mgQ@mail.gmail.com","threadId":"31414","inReplyTo":"7v7gscgnnr.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 0/9] new git check-ignore sub-command","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-06T17:44:34Z","receivedAt":"2012-09-06T17:44:34Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Sun, Sep 2, 2012 at 9:35 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Adam Spiers <git@adamspiers.org> writes:\n>\n>> I was browsing stackoverflow the other day and came across this question:\n>>\n>>     http://stackoverflow.com/questions/12144633/which-gitignore-rule-is-ignoring-my-file/\n>>\n>> A quick google revealed this thread from 2009:\n>>\n>>     http://thread.gmane.org/gmane.comp.version-control.git/108671/focus=108815\n>>\n>> where Junio and Jeff discussed the possibility of adding a new `git\n>> check-ignore' subcommand somewhat analogous to the existing `git\n>> check-attr', and suggested the beginnings of an implementation.  It\n>> struck me that it might not be too hard to follow these ideas to their\n>> natural conclusion, so I decided it would make a fun project :-)\n>\n> Thanks.  I wish there are more people like you ;-)\n\nThanks ;-)\n\n> As to styles, I spotted only three kinds of \"Huh?\":\n>\n>  * do not initialise statics to 0 or NULL, e.g.\n>\n>     -static int exclude_args = 0;\n>     +static int exclude_args;\n>\n>  * avoid unnnecessary braces {} around single statement blocks, e.g.\n>\n>     -if (exclude) {\n>     +if (exclude)\n>         return exclude;\n>     -}\n>\n>  * else should follow close brace '}' of if clause, e.g.\n>\n>      if (...) {\n>          ...\n>     -}\n>     -else {\n>     +} else {\n>          ...\n\nOK thanks, I will fix these and also submit a patch for CodingGuidelines.\n"},{"id":"198467","messageId":"CAOkDyE-u7yzEDu++iX_zRMU==oUWbR5uM1j50N5XH4SaJkB0Ww@mail.gmail.com","threadId":"31414","inReplyTo":"A05E4811A66D499DA6AC932AC49F1E8F@PhilipOakley","subject":"Re: [PATCH 6/9] For each exclude pattern, store information about where it came from","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-06T17:56:10Z","receivedAt":"2012-09-06T17:56:10Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Sun, Sep 2, 2012 at 11:36 PM, Philip Oakley <philipoakley@iee.org> wrote:\n> From: \"Junio C Hamano\" <gitster@pobox.com>\n> Sent: Sunday, September 02, 2012 8:02 PM\n>> \"Philip Oakley\" <philipoakley@iee.org> writes:\n>>> Is there a way to identify the config core.excludesfile if present?\n>>> i.e. that it is from that config variable, rather than directory\n>>> traversal.\n\nYes, the output of git check-ignore includes the source file, so you\ncan easily see whether the ignore originated from a per-directory\nexclude or from core.excludesfile.  One giveaway is that the former\nis an absolute path, and the latter are all relative.\n\n>> If the code handles $GIT_DIR/info/exclude then that configuration\n>> would also be handled the same way, no?\n\nYes, they are both handled via setup_standard_excludes().\n\n> If I've understood the regular code correctly, the core.excludesfile is\n> always at one end of the exclude struct so should be easy to check at that\n> position.\n\nIt's already checked in my patch series, and the tests cover this case:\n\n  https://github.com/aspiers/git/blob/check-ignore/t/t0007-ignores.sh#L214\n\nHaving said that, I forgot to make them check $GIT_DIR/info/exclude.\nI'll fix that.\n"},{"id":"198480","messageId":"7vvcfq273l.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"CACsJy8CoBvxg9SWFFXbuDJM7Lu74Jw6LVc=7cNwu24h7pijeXQ@mail.gmail.com","subject":"Re: [PATCH 5/9] Refactor excluded and path_excluded","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-06T21:07:26Z","receivedAt":"2012-09-06T21:07:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n>>> I'm not great at naming. And path_excluded() cannot be reused to avoid\n>>> problems with other ongoing series if any. So path_is_excluded()?\n>>\n>> is_path_excluded()?\n>\n> Good too.\n\nSure, either would work.\n"},{"id":"198513","messageId":"CAOkDyE8mEQ5XkH5TBsrVkAZOqoT+DVoggeGawH=cqU6E6=hv=g@mail.gmail.com","threadId":"31414","inReplyTo":"7v7gscgnnr.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 0/9] new git check-ignore sub-command","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-07T10:03:38Z","receivedAt":"2012-09-07T10:03:38Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Sun, Sep 2, 2012 at 9:35 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>  * avoid unnnecessary braces {} around single statement blocks, e.g.\n>\n>     -if (exclude) {\n>     +if (exclude)\n>         return exclude;\n>     -}\n>\n>  * else should follow close brace '}' of if clause, e.g.\n>\n>      if (...) {\n>          ...\n>     -}\n>     -else {\n>     +} else {\n>          ...\n\nWhat about when the if clause requires braces but the else clause\ndoesn't?  Should it be\n\n\tif (...) {\n\t\t...;\n\t\t...;\n\t} else\n\t\t...;\n\nor\n\n\tif (...) {\n\t\t...;\n\t\t...;\n\t}\n\telse\n\t\t...;\n\n?\n"},{"id":"198530","messageId":"7v627pzsr7.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"CAOkDyE8mEQ5XkH5TBsrVkAZOqoT+DVoggeGawH=cqU6E6=hv=g@mail.gmail.com","subject":"Re: [PATCH 0/9] new git check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-07T16:45:32Z","receivedAt":"2012-09-07T16:45:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n> On Sun, Sep 2, 2012 at 9:35 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>>  * avoid unnnecessary braces {} around single statement blocks, e.g.\n>>\n>>     -if (exclude) {\n>>     +if (exclude)\n>>         return exclude;\n>>     -}\n>>\n>>  * else should follow close brace '}' of if clause, e.g.\n>>\n>>      if (...) {\n>>          ...\n>>     -}\n>>     -else {\n>>     +} else {\n>>          ...\n>\n> What about when the if clause requires braces but the else clause\n> doesn't?  Should it be\n>\n> \tif (...) {\n> \t\t...;\n> \t\t...;\n> \t} else\n> \t\t...;\n>\n> or\n>\n> \tif (...) {\n> \t\t...;\n> \t\t...;\n> \t}\n> \telse\n> \t\t...;\n>\n> ?\n\nNeither.  We try to do (but often fail ;-)\n\n\tif (...) {\n        \t...;\n        \t...;\n\t} else {\n        \t...;\n\t}\n\nfollowing the kernel style, unless there is good reason not to.\n"},{"id":"198665","messageId":"20120910110928.GA12974@atlantic.linksys.moosehall","threadId":"31414","inReplyTo":"CACsJy8A2-C9xSz2LXt9Ptjxhe++i2vcBSMY-cxJLWUiutajZUQ@mail.gmail.com","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-10T11:09:29Z","receivedAt":"2012-09-10T11:09:29Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Tue, Sep 04, 2012 at 08:06:12PM +0700, Nguyen Thai Ngoc Duy wrote:\n> On Sun, Sep 2, 2012 at 7:12 AM, Adam Spiers <git@adamspiers.org> wrote:\n> > --- a/builtin/add.c\n> > +++ b/builtin/add.c\n> > @@ -273,7 +273,7 @@ static int add_files(struct dir_struct *dir, int flags)\n> >                 fprintf(stderr, _(ignore_error));\n> >                 for (i = 0; i < dir->ignored_nr; i++)\n> >                         fprintf(stderr, \"%s\\n\", dir->ignored[i]->name);\n> > -               fprintf(stderr, _(\"Use -f if you really want to add them.\\n\"));\n> > +               fprintf(stderr, _(\"Use -f if you really want to add them, or git check-ignore to see\\nwhy they're ignored.\\n\"));\n> >                 die(_(\"no files added\"));\n> >         }\n> \n> String too long (> 80 chars).\n\nYou mean the line of code is too long, or the argument to _(), or\nboth?  I didn't like this either, but I saw that builtin/checkout.c\nalready did something similar twice, and I wasn't sure how else to do\nit.  Suggestions gratefully received.\n\n> > +static const char * const check_ignore_usage[] = {\n> > +\"git check-ignore pathname...\",\n> > +\"git check-ignore --stdin [-z] < <list-of-paths>\",\n> > +NULL\n> > +};\n> > +\n> > +static const struct option check_ignore_options[] = {\n> > +       OPT_BOOLEAN(0 , \"stdin\", &stdin_paths, \"read file names from stdin\"),\n> > +       OPT_BOOLEAN('z', NULL, &null_term_line,\n> > +               \"input paths are terminated by a null character\"),\n> > +       OPT_END()\n> > +};\n> \n> You may want to mark help strings (\"read file names from stdin\" and\n> \"input paths... null character\") and check_ignore_usage[] for\n> translation. Just wrap those strings with N_() and you'll be fine. For\n> similar changes, check out nd/i18n-parseopt-help on branch 'pu'.\n\nThanks, I'll do that.\n\n[snipped discussion of \"include\" / \"exclude\" which already continued elsewhere]\n\n> > +static void check_ignore(const char *prefix, const char **pathspec)\n> > +{\n> > +       struct dir_struct dir;\n> > +       const char *path;\n> > +       char *seen = NULL;\n> > +\n> > +       /* read_cache() is only necessary so we can watch out for submodules. */\n> > +       if (read_cache() < 0)\n> > +               die(_(\"index file corrupt\"));\n> > +\n> > +       memset(&dir, 0, sizeof(dir));\n> > +       dir.flags |= DIR_COLLECT_IGNORED;\n> > +       setup_standard_excludes(&dir);\n> \n> You should support ignore rules from files and command line arguments\n> too, like ls-files. For quick testing.\n\nYou mean --exclude, --exclude-from, and --exclude-per-directory?\nSure, although I have limited time right now, so maybe these could be\nadded in a later iteration?\n\n> > +static NORETURN void error_with_usage(const char *msg)\n> > +{\n> > +       error(\"%s\", msg);\n> > +       usage_with_options(check_ignore_usage, check_ignore_options);\n> > +}\n> \n> Interesting. We have usage_msg_opt() in parse-options.c, but it's more\n> verbose. Perhaps this function should be moved to parse-options.c\n> because it may be useful to other commands as well?\n\nI'll look into it.\n"},{"id":"198666","messageId":"20120910111501.GB12974@atlantic.linksys.moosehall","threadId":"31414","inReplyTo":"CACsJy8CPaJKGU0Lyjr93s9JEc_VBuLrHNH=nRa42K8Ohj3OWFA@mail.gmail.com","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-10T11:15:01Z","receivedAt":"2012-09-10T11:15:01Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Wed, Sep 05, 2012 at 05:25:25PM +0700, Nguyen Thai Ngoc Duy wrote:\n> On Wed, Sep 5, 2012 at 12:26 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> > Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n> >\n> >>> +static void output_exclude(const char *path, struct exclude *exclude)\n> >>> +{\n> >>> +       char *type = exclude->to_exclude ? \"excluded\" : \"included\";\n> >>> +       char *bang = exclude->to_exclude ? \"\" : \"!\";\n> >>> +       char *dir  = (exclude->flags & EXC_FLAG_MUSTBEDIR) ? \"/\" : \"\";\n> >>> +       printf(_(\"%s: %s %s%s%s \"), path, type, bang, exclude->pattern, dir);\n> >>\n> >> These English words \"excluded\" and \"included\" make the translator me\n> >> want to translate them. But they could be the markers for scripts, so\n> >> they may not be translated. How about using non alphanumeric letters\n> >> instead?\n> >\n> > I agree they should not be translated, but it is a disease to think\n> > unintelligible mnemonic is a better input format for scripts than\n> > the spelled out words.  \"excluded/included\" pair is just fine.\n> \n> Not all mnemonic is unintelligible though. \"+\" and \"-\" may fit well in\n> this case. I'm just trying to make sure we have checked the mnemonic\n> pool before ending up with excluded/included.\n\nPersonally I'd be against introducing \"+\" and \"-\" when we already have\n\"!\" and \"\".  Even though \"+\" and \"-\" are more intuitive, it would\ncreate inconsistency and IMHO confusion.\n\nI'm still unconvinced that it's worth having a separate type field in\nthe output when the pattern field already has a \"!\" prefix for\ninclusions.  Does a separate field really help porcelain writers or\nmake the output more readable?\n"},{"id":"198670","messageId":"CACsJy8Dwpx-Qs0+du6rHvbwL_A4H_DqBMP4umhBiDK2Fc8icww@mail.gmail.com","threadId":"31414","inReplyTo":"20120910110928.GA12974@atlantic.linksys.moosehall","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-10T12:25:07Z","receivedAt":"2012-09-10T12:25:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Sep 10, 2012 at 6:09 PM, Adam Spiers <git@adamspiers.org> wrote:\n>> >                         fprintf(stderr, \"%s\\n\", dir->ignored[i]->name);\n>> > -               fprintf(stderr, _(\"Use -f if you really want to add them.\\n\"));\n>> > +               fprintf(stderr, _(\"Use -f if you really want to add them, or git check-ignore to see\\nwhy they're ignored.\\n\"));\n>> >                 die(_(\"no files added\"));\n>> >         }\n>>\n>> String too long (> 80 chars).\n>\n> You mean the line of code is too long, or the argument to _(), or\n> both?  I didn't like this either, but I saw that builtin/checkout.c\n> already did something similar twice, and I wasn't sure how else to do\n> it.  Suggestions gratefully received.\n\nI don't rememeber :( I might mean the output because I missed \"\\n\" in\nthe middle. At least you can split the string in to at \"\\n\" to make it\nresemble output.\n\n>> You should support ignore rules from files and command line arguments\n>> too, like ls-files. For quick testing.\n>\n> You mean --exclude, --exclude-from, and --exclude-per-directory?\n> Sure, although I have limited time right now, so maybe these could be\n> added in a later iteration?\n\nSure, no problem. It's not hard to add them anyway (I think).\n-- \nDuy\n"},{"id":"198690","messageId":"7vwr01on5o.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"20120910110928.GA12974@atlantic.linksys.moosehall","subject":"Re: [PATCH 9/9] Add git-check-ignores","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-10T16:30:59Z","receivedAt":"2012-09-10T16:30:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\nAdministrivia.  Please never deflect direct responses to you with\n\"Mail-Followup-To\" header.  I told my mailer to \"follow-up\" so that I\ncould give you advice in response, while adding others in the\ndiscussion to Cc so that they do not have to repeat what I said, but\nyour \"Mail-follow-up-to\" forced my advice to go to Nguyen, who does\nnot need one.\n\n> On Tue, Sep 04, 2012 at 08:06:12PM +0700, Nguyen Thai Ngoc Duy wrote:\n>> On Sun, Sep 2, 2012 at 7:12 AM, Adam Spiers <git@adamspiers.org> wrote:\n>> > --- a/builtin/add.c\n>> > +++ b/builtin/add.c\n>> > @@ -273,7 +273,7 @@ static int add_files(struct dir_struct *dir, int flags)\n>> >                 fprintf(stderr, _(ignore_error));\n>> >                 for (i = 0; i < dir->ignored_nr; i++)\n>> >                         fprintf(stderr, \"%s\\n\", dir->ignored[i]->name);\n>> > -               fprintf(stderr, _(\"Use -f if you really want to add them.\\n\"));\n>> > +               fprintf(stderr, _(\"Use -f if you really want to add them, or git check-ignore to see\\nwhy they're ignored.\\n\"));\n>> >                 die(_(\"no files added\"));\n>> >         }\n>> \n>> String too long (> 80 chars).\n>\n> You mean the line of code is too long, or the argument to _(), or\n> both?\n\nBoth.\n\n               fprintf(stderr, _(\"Use -f if you really want to add them, or\\n\"\n\t\t\t\t \"run git check-ignore to see\\nwhy they're ignored.\\n\"));\n\nBut in this particular case, I tend to think the additional noise\ndoes not add much value and something the user wouldn't want to see\nover and over again (in other words, it belongs to \"an advice\").\n"},{"id":"199513","messageId":"1348081202-17361-1-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"7v627pzsr7.fsf@alter.siamese.dyndns.org","subject":"[PATCH] Document conventions on static initialization and else cuddling","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-19T19:00:02Z","receivedAt":"2012-09-19T19:00:02Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"Signed-off-by: Adam Spiers <git@adamspiers.org>\n---\n\nI have begun work on fixing existing code to adhere to these\nguidelines on braces, but there are currently a lot of violations,\nwhich means any patches to fix them would be large.  So before I spend\nany more time on it, I would like to check whether such patches would\nbe welcomed?  And if so, should I be doing that on the master branch?\n\nI have also added some simple rules such as `check-brace-before-else'\nto the top-level Makefile which perform appropriate `grep -n' commands\nto detect violations and for example easily fix them via emacs' M-x\ngrep.  These rules can be invoked together via a `check-style' target.\nWould this also be welcomed?  If so, should the checks all be\nintroduced in a single commit, or each check along with the code which\nwas fixed with its help?\n\nBTW I briefly tried to find an existing tool out there which could\nalready do the checking for us, but only found ones like uncrustify\nwhich rewrite code rather than warning on inconsistencies.  I also saw\nthat the kernel's scripts/checkpatch.pl only worked with patches and\nwas also extremely kernel-specific in the nature of its checks.\n\n Documentation/CodingGuidelines | 42 +++++++++++++++++++++++++++++++++++++-----\n 1 file changed, 37 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines\nindex 57da6aa..1a2851d 100644\n--- a/Documentation/CodingGuidelines\n+++ b/Documentation/CodingGuidelines\n@@ -117,17 +117,49 @@ For C programs:\n    \"char * string\".  This makes it easier to understand code\n    like \"char *string, c;\".\n \n+ - We avoid unnecessary explicit initialization of BSS-allocated vars\n+   (static and globals) to zero or NULL:\n+\n+\tstatic int n;\n+\tstatic char **ch;\n+\n+   rather than:\n+\n+\tstatic int n = 0;\n+\tstatic char **ch = NULL;\n+\n+   These are superfluous according to ISO/IEC 9899:1999 (a.k.a. C99);\n+   see item 10 in section 6.7.8 (\"Initialization\") of WG14 N1256 for\n+   the exact text:\n+\n+     http://open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf\n+\n  - We avoid using braces unnecessarily.  I.e.\n \n \tif (bla) {\n \t\tx = 1;\n \t}\n \n-   is frowned upon.  A gray area is when the statement extends\n-   over a few lines, and/or you have a lengthy comment atop of\n-   it.  Also, like in the Linux kernel, if there is a long list\n-   of \"else if\" statements, it can make sense to add braces to\n-   single line blocks.\n+   is frowned upon.  A gray area is when the statement extends over a\n+   few lines, and/or you have a lengthy comment atop of it.  Also,\n+   like in the Linux kernel, it can make sense to add braces to single\n+   line blocks if there are already braces in another branch of the\n+   same conditional, and/or there is long list of \"else if\"\n+   statements.\n+\n+ - When braces are required, we cuddle \"else\" and \"else if\", so the\n+   preceding closing brace appears on the same line, e.g.\n+\n+\tif (foo) {\n+\t\t...;\n+\t} else if (bar) {\n+\t\t...;\n+\t\t...;\n+\t} else {\n+\t\t...;\n+\t}\n+\n+   following Linux kernel style, unless there is a good reason not to.\n \n  - We try to avoid assignments inside if().\n \n-- \n1.7.12.147.g6d168f4\n"},{"id":"199534","messageId":"7vk3vpg2v1.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"1348081202-17361-1-git-send-email-git@adamspiers.org","subject":"Re: [PATCH] Document conventions on static initialization and else cuddling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-19T20:43:46Z","receivedAt":"2012-09-19T20:43:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n> Signed-off-by: Adam Spiers <git@adamspiers.org>\n> ---\n>\n> I have begun work on fixing existing code to adhere to these\n> guidelines on braces, but there are currently a lot of violations,\n> which means any patches to fix them would be large.  So before I spend\n> any more time on it, I would like to check whether such patches would\n> be welcomed? And if so, should I be doing that on the master branch?\n\nIn general, no.\n\nThe cost/benefit ratio of carrying such a change is very high.\n\nThe guidelines are not a set of hard rules that any and all code in\nviolation have to be fixed right away.  I am reluctant to pile more\ndetailed \"rules\" in the existing list in the document to give it an\nincorrect impression that they are hard rules that all lines should\nabide by, which inevitably leads to people who end up wasting time\non creating \"style fixes\" patches whose only practical effect is to\ncollide with other topics in flight.\n\nEspecially if such a \"style fix\" is done as a single patch, it will\nblock all the other topics that happen to touch the overlapping\nregions for reasons more important than \"style fix\".\n\nTo mitigate the risk of such unnecessary collisions, while improving\nthe health of the codebase, we tend to do two things:\n\n - review new code and make sure it does not introduce guideline\n   violations.  Two sets of new code that touch overlapping area by\n   definition have to collide and their conflicts resolved anyway,\n   so this does not add any additional cost to the project.\n\n - before updating existing code, clean up the area and small\n   surrounding area it touches as a preparation step of the patch\n   series.  As long as \"surrounding area\" is kept sufficiently small\n   (the preparation step may have to refrain from touching the whole\n   file, depending on what other topics are still in flight), this\n   again does not add any additional cost to the project.\n\nThere is a third-option that we tend try to avoid but occasionally\ndo take:\n\n - update a dormant code that nobody has touched for a long time and\n   no discussion on the list (with or without patch) is likely to\n   result in a new topic that might want to touch it.\n\nBy the way, I have a handful of my favorite styles that are not\nlisted in the document, and when I write new code myself I try to\nstick to them, but I do not raise a red (or any color) flag when\nseeing \"violating\" code in a new patch [*1*].\n\n> I have also added some simple rules such as `check-brace-before-else'\n> to the top-level Makefile which perform appropriate `grep -n' commands\n> to detect violations and for example easily fix them via emacs' M-x\n> grep.  These rules can be invoked together via a `check-style' target.\n> Would this also be welcomed?  If so, should the checks all be\n> introduced in a single commit, or each check along with the code which\n> was fixed with its help?\n\nI am not enthused, personally.\n\n> BTW I briefly tried to find an existing tool out there which could\n> already do the checking for us, but only found ones like uncrustify\n> which rewrite code rather than warning on inconsistencies.  I also saw\n> that the kernel's scripts/checkpatch.pl only worked with patches and\n> was also extremely kernel-specific in the nature of its checks.\n\nThe checkpatch.pl script works with patches for a very good\nreason. It is a tool to look for \"new\" problems patches\nintroduce.\n\nI often run \"checkpatch.pl --no-tree\" on incoming patches, by the\nway.\n\n>  Documentation/CodingGuidelines | 42 +++++++++++++++++++++++++++++++++++++-----\n>  1 file changed, 37 insertions(+), 5 deletions(-)\n>\n> diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines\n> index 57da6aa..1a2851d 100644\n> --- a/Documentation/CodingGuidelines\n> +++ b/Documentation/CodingGuidelines\n> @@ -117,17 +117,49 @@ For C programs:\n>     \"char * string\".  This makes it easier to understand code\n>     like \"char *string, c;\".\n>  \n> + - We avoid unnecessary explicit initialization of BSS-allocated vars\n> +   (static and globals) to zero or NULL:\n> +\n> +\tstatic int n;\n> +\tstatic char **ch;\n> +\n> +   rather than:\n> +\n> +\tstatic int n = 0;\n> +\tstatic char **ch = NULL;\n\nAs I said, I am in general not in favor of piling more rules here,\nbut we've seen this one in new contribution often enough that it is\na good addition.\n\n> +\n> +   These are superfluous according to ISO/IEC 9899:1999 (a.k.a. C99);\n> +   see item 10 in section 6.7.8 (\"Initialization\") of WG14 N1256 for\n> +   the exact text:\n> +\n> +     http://open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf\n\nBut I do not think this explanation is necessary.  \"This is one of\nour house rules\" is a sufficient reason people, who want to blindly\nobey guidelines, need to see.\n\n>   - We avoid using braces unnecessarily.  I.e.\n>  \n>  \tif (bla) {\n>  \t\tx = 1;\n>  \t}\n>  \n> -   is frowned upon.  A gray area is when the statement extends\n> -   over a few lines, and/or you have a lengthy comment atop of\n> -   it.  Also, like in the Linux kernel, if there is a long list\n> -   of \"else if\" statements, it can make sense to add braces to\n> -   single line blocks.\n> +   is frowned upon.  A gray area is when the statement extends\n> +   over a few lines, and/or you have a lengthy comment atop of\n> +   it.  Also, like in the Linux kernel, it can make sense to add\n> +   braces to single line blocks if there are already braces in\n> +   another branch of the same conditional, and/or there is long\n> +   list of \"else if\" statements.\n\nReflowing text without reason is frowned upon as it makes the\nreview unnecessary harder by hiding where things have changed.\n\nYou are suggesting to\n\n        /* Turn this into ... */        /* ... this instead */\n        if (condition)                  if (condition) {\n                true;                           true;\n        else {                          } else {\n                otherwise;                      otherwise;\n                ...                             ...\n        }                               }\n\nI do not think such an update is wrong per-se, but among different\nshades of gray, the left is probably one of the most minor kind of\nviolations.\n\n\n\n[Footnote]\n\n*1* An example of them is \"when 'if (...) ... else ...' chooses\nbetween two equally plausible conditions, write it in such a way\nthat the body of the else clause runs longer\".  That is because\nit is far easier to get confused about the condition when looking at\nwhat the body in the \"else\" clause is doing after a long body in the\n\"if\" clause:\n\n        /* BAD */                       /* GOOD */\n        if (condition) {                if (!condition) {\n            ....                                ....\n            ....                                ....\n            ....                        } else {\n            ....                                ....\n            ....                                ....\n            ....                                ....\n            ....                                ....\n        } else {                                ....\n            ....                                ....\n            ....                                ....\n        }                               }\n\nBut that can safely be done only when \"condition\" and \"!condition\"\nare both equally natural expressions.\n"},{"id":"199539","messageId":"20120919211403.GF19246@atlantic.linksys.moosehall","threadId":"31414","inReplyTo":"7vk3vpg2v1.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] Document conventions on static initialization and else cuddling","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-19T21:14:03Z","receivedAt":"2012-09-19T21:14:03Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Wed, Sep 19, 2012 at 01:43:46PM -0700, Junio C Hamano wrote:\n> Adam Spiers <git@adamspiers.org> writes:\n> \n> > Signed-off-by: Adam Spiers <git@adamspiers.org>\n> > ---\n> >\n> > I have begun work on fixing existing code to adhere to these\n> > guidelines on braces, but there are currently a lot of violations,\n> > which means any patches to fix them would be large.  So before I spend\n> > any more time on it, I would like to check whether such patches would\n> > be welcomed? And if so, should I be doing that on the master branch?\n> \n> In general, no.\n\n[snipped]\n\n> > I have also added some simple rules such as `check-brace-before-else'\n> > to the top-level Makefile which perform appropriate `grep -n' commands\n> > to detect violations and for example easily fix them via emacs' M-x\n> > grep.  These rules can be invoked together via a `check-style' target.\n> > Would this also be welcomed?  If so, should the checks all be\n> > introduced in a single commit, or each check along with the code which\n> > was fixed with its help?\n> \n> I am not enthused, personally.\n\nOK, I'll drop work on these then.\n\n[snipped]\n\n> >  Documentation/CodingGuidelines | 42 +++++++++++++++++++++++++++++++++++++-----\n> >  1 file changed, 37 insertions(+), 5 deletions(-)\n> >\n> > diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines\n> > index 57da6aa..1a2851d 100644\n> > --- a/Documentation/CodingGuidelines\n> > +++ b/Documentation/CodingGuidelines\n> > @@ -117,17 +117,49 @@ For C programs:\n> >     \"char * string\".  This makes it easier to understand code\n> >     like \"char *string, c;\".\n> >  \n> > + - We avoid unnecessary explicit initialization of BSS-allocated vars\n> > +   (static and globals) to zero or NULL:\n> > +\n> > +\tstatic int n;\n> > +\tstatic char **ch;\n> > +\n> > +   rather than:\n> > +\n> > +\tstatic int n = 0;\n> > +\tstatic char **ch = NULL;\n> \n> As I said, I am in general not in favor of piling more rules here,\n> but we've seen this one in new contribution often enough that it is\n> a good addition.\n\nYes, I am proposing its addition precisely because its current\nomission resulted in a small but non-negligible amount of both my time\nand yours being wasted in a recent patch review cycle.\n\n> > +\n> > +   These are superfluous according to ISO/IEC 9899:1999 (a.k.a. C99);\n> > +   see item 10 in section 6.7.8 (\"Initialization\") of WG14 N1256 for\n> > +   the exact text:\n> > +\n> > +     http://open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf\n> \n> But I do not think this explanation is necessary.  \"This is one of\n> our house rules\" is a sufficient reason people, who want to blindly\n> obey guidelines, need to see.\n\nI presume I'm not the only person who wishes to obey guidelines but\nalso understand why they exist.  In general, a deeper understanding of\nthe purpose behind guidelines helps improve judgement about when and\nhow to apply them.\n\n> >   - We avoid using braces unnecessarily.  I.e.\n> >  \n> >  \tif (bla) {\n> >  \t\tx = 1;\n> >  \t}\n> >  \n> > -   is frowned upon.  A gray area is when the statement extends\n> > -   over a few lines, and/or you have a lengthy comment atop of\n> > -   it.  Also, like in the Linux kernel, if there is a long list\n> > -   of \"else if\" statements, it can make sense to add braces to\n> > -   single line blocks.\n> > +   is frowned upon.  A gray area is when the statement extends\n> > +   over a few lines, and/or you have a lengthy comment atop of\n> > +   it.  Also, like in the Linux kernel, it can make sense to add\n> > +   braces to single line blocks if there are already braces in\n> > +   another branch of the same conditional, and/or there is long\n> > +   list of \"else if\" statements.\n> \n> Reflowing text without reason is frowned upon as it makes the\n> review unnecessary harder by hiding where things have changed.\n\nThere was a reason, it just wasn't a particularly good one :-) (emacs\nreflows by paragraph, so you have to go out of your way to stop it\nreflowing existing text.)\n\nBTW, for anyone else reading who is confused by the fact that the\nquoted hunk above shows no signs of reflowing, I think the hunk itself\nhas been re-reflowed back to the original width (presumably\ndeliberately in order to determine what really changed).\n\n> You are suggesting to\n> \n>         /* Turn this into ... */        /* ... this instead */\n>         if (condition)                  if (condition) {\n>                 true;                           true;\n>         else {                          } else {\n>                 otherwise;                      otherwise;\n>                 ...                             ...\n>         }                               }\n> \n> I do not think such an update is wrong per-se, but among different\n> shades of gray, the left is probably one of the most minor kind of\n> violations.\n\nI was under the impression it was _your_ suggestion ;-)\n\n  http://thread.gmane.org/gmane.comp.version-control.git/204661/focus=204975\n\nPerhaps I read too much into that, since it was concerning the mirror\nimage case (i.e. where the 'else' branch is a single statement and the\n'if' branch requires braces), but they seem like stylistically\nequivalent debates to me.\n\nThanks for the review, Junio.  Based on my responses above, what would\nyou like me to do with the patch (if anything)?\n"},{"id":"199609","messageId":"1348170383-15751-1-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"7vvcfwf937.fsf@alter.siamese.dyndns.org","subject":"[PATCH v2 00/14] new git check-ignore sub-command","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:09Z","receivedAt":"2012-09-20T19:46:09Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"This is a re-vamp of my original check-ignore series, which aims to\naddress all the feedback which was raised in the first round of\nreviews.  The most notable changes are the CLI options and output\nformats as suggested by Junio and Nguyễn; now there are three levels\nof verbosity: --quiet, default, and --verbose.  -z also now affects\nthe output and so is now compatible with the --stdin optin.\n\nSome commits have been broken into smaller pieces to facilitate easier\nreviews, and based on an earlier discussion, three exclude functions\nhave been given an 'is_' prefix to clarify their boolean nature.  The\nhelper functions extracted from these three now have more meaningful\nnames rather than just a '_1' suffix.\n\nOther minor issues, such as inconsistent coding style, have been\nfixed, and the modification to the output text in add.c has been\nscrapped.\n\nIt has been rebased on the latest master, and passed a full test run.\n\nAdam Spiers (14):\n  Update directory listing API doc to match code\n  Improve documentation and comments regarding directory traversal API\n  Rename cryptic 'which' variable to more consistent name\n  Rename path_excluded() to is_path_excluded()\n  Rename excluded_from_list() to is_excluded_from_list()\n  Rename excluded() to is_excluded()\n  Refactor is_excluded_from_list()\n  Refactor is_excluded()\n  Refactor is_path_excluded()\n  For each exclude pattern, store information about where it came from\n  Refactor treat_gitlinks()\n  Extract some useful pathspec handling code from builtin/add.c into a\n    library\n  Provide free_directory() for reclaiming dir_struct memory\n  Add git-check-ignore sub-command\n\n .gitignore                                        |   1 +\n Documentation/git-check-ignore.txt                |  85 ++++\n Documentation/gitignore.txt                       |   6 +-\n Documentation/technical/api-directory-listing.txt |  23 +-\n Makefile                                          |   3 +\n attr.c                                            |   2 +-\n builtin.h                                         |   1 +\n builtin/add.c                                     |  84 +---\n builtin/check-ignore.c                            | 167 ++++++\n builtin/clean.c                                   |   2 +-\n builtin/ls-files.c                                |   5 +-\n command-list.txt                                  |   1 +\n contrib/completion/git-completion.bash            |   1 +\n dir.c                                             | 191 +++++--\n dir.h                                             |  47 +-\n git.c                                             |   1 +\n pathspec.c                                        |  97 ++++\n pathspec.h                                        |   6 +\n t/t0007-ignores.sh                                | 587 ++++++++++++++++++++++\n t/t9902-completion.sh                             |  24 +-\n unpack-trees.c                                    |  10 +-\n 21 files changed, 1182 insertions(+), 162 deletions(-)\n create mode 100644 Documentation/git-check-ignore.txt\n create mode 100644 builtin/check-ignore.c\n create mode 100644 pathspec.c\n create mode 100644 pathspec.h\n create mode 100755 t/t0007-ignores.sh\n\n-- \n1.7.12.147.g6d168f4\n"},{"id":"199607","messageId":"1348170383-15751-2-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 01/14] Update directory listing API doc to match code","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:10Z","receivedAt":"2012-09-20T19:46:10Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"7c4c97c0ac turned the flags in struct dir_struct into a single bitfield\nvariable, but forgot to update this document.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n Documentation/technical/api-directory-listing.txt | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex add6f43..0356d25 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -17,24 +17,24 @@ options are:\n \tThe name of the file to be read in each directory for excluded\n \tfiles (typically `.gitignore`).\n \n-`collect_ignored`::\n+`flags`::\n \n-\tInclude paths that are to be excluded in the result.\n+\tA bit-field of options:\n \n-`show_ignored`::\n+`DIR_SHOW_IGNORED`:::\n \n \tThe traversal is for finding just ignored files, not unignored\n \tfiles.\n \n-`show_other_directories`::\n+`DIR_SHOW_OTHER_DIRECTORIES`:::\n \n \tInclude a directory that is not tracked.\n \n-`hide_empty_directories`::\n+`DIR_HIDE_EMPTY_DIRECTORIES`:::\n \n \tDo not include a directory that is not tracked and is empty.\n \n-`no_gitlinks`::\n+`DIR_NO_GITLINKS`:::\n \n \tIf set, recurse into a directory that looks like a git\n \tdirectory.  Otherwise it is shown as a directory.\n-- \n1.7.12.147.g6d168f4\n"},{"id":"199621","messageId":"1348170383-15751-3-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 02/14] Improve documentation and comments regarding directory traversal API","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:11Z","receivedAt":"2012-09-20T19:46:11Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":">From the perspective of a newcomer to the codebase, the directory\ntraversal API has a few potentially confusing properties.  These\ncomments clarify a few key aspects and will hopefully make it easier\nto understand for other newcomers in the future.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n Documentation/technical/api-directory-listing.txt |  9 +++++---\n dir.c                                             |  8 ++++++-\n dir.h                                             | 26 +++++++++++++++++++++--\n 3 files changed, 37 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex 0356d25..944fc39 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -9,8 +9,11 @@ Data structure\n --------------\n \n `struct dir_struct` structure is used to pass directory traversal\n-options to the library and to record the paths discovered.  The notable\n-options are:\n+options to the library and to record the paths discovered.  A single\n+`struct dir_struct` is used regardless of whether or not the traversal\n+recursively descends into subdirectories.\n+\n+The notable options are:\n \n `exclude_per_dir`::\n \n@@ -39,7 +42,7 @@ options are:\n \tIf set, recurse into a directory that looks like a git\n \tdirectory.  Otherwise it is shown as a directory.\n \n-The result of the enumeration is left in these fields::\n+The result of the enumeration is left in these fields:\n \n `entries[]`::\n \ndiff --git a/dir.c b/dir.c\nindex 4868339..98d1995 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -2,6 +2,8 @@\n  * This handles recursive filename detection with exclude\n  * files, index knowledge etc..\n  *\n+ * See Documentation/technical/api-directory-listing.txt\n+ *\n  * Copyright (C) Linus Torvalds, 2005-2006\n  *\t\t Junio Hamano, 2005-2006\n  */\n@@ -451,6 +453,10 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)\n \t\tdie(\"cannot use %s as an exclude file\", fname);\n }\n \n+/*\n+ * Loads the per-directory exclude list for the substring of base\n+ * which has a char length of baselen.\n+ */\n static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n {\n \tstruct exclude_list *el;\n@@ -461,7 +467,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t    (baselen + strlen(dir->exclude_per_dir) >= PATH_MAX))\n \t\treturn; /* too long a path -- ignore */\n \n-\t/* Pop the ones that are not the prefix of the path being checked. */\n+\t/* Pop the directories that are not the prefix of the path being checked. */\n \tel = &dir->exclude_list[EXC_DIRS];\n \twhile ((stk = dir->exclude_stack) != NULL) {\n \t\tif (stk->baselen <= baselen &&\ndiff --git a/dir.h b/dir.h\nindex 893465a..a226fbc 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -1,6 +1,8 @@\n #ifndef DIR_H\n #define DIR_H\n \n+/* See Documentation/technical/api-directory-listing.txt */\n+\n #include \"strbuf.h\"\n \n struct dir_entry {\n@@ -12,6 +14,12 @@ struct dir_entry {\n #define EXC_FLAG_ENDSWITH 4\n #define EXC_FLAG_MUSTBEDIR 8\n \n+/*\n+ * Each .gitignore file will be parsed into patterns which are then\n+ * appended to the relevant exclude_list (either EXC_DIRS or\n+ * EXC_FILE).  exclude_lists are also used to represent the list of\n+ * --exclude values passed via CLI args (EXC_CMDL).\n+ */\n struct exclude_list {\n \tint nr;\n \tint alloc;\n@@ -26,9 +34,15 @@ struct exclude_list {\n \t} **excludes;\n };\n \n+/*\n+ * The contents of the per-directory exclude files are lazily read on\n+ * demand and then cached in memory, one per exclude_stack struct, in\n+ * order to avoid opening and parsing each one every time that\n+ * directory is traversed.\n+ */\n struct exclude_stack {\n-\tstruct exclude_stack *prev;\n-\tchar *filebuf;\n+\tstruct exclude_stack *prev; /* the struct exclude_stack for the parent directory */\n+\tchar *filebuf; /* remember pointer to per-directory exclude file contents so we can free() */\n \tint baselen;\n \tint exclude_ix;\n };\n@@ -59,6 +73,14 @@ struct dir_struct {\n #define EXC_DIRS 1\n #define EXC_FILE 2\n \n+\t/*\n+\t * Temporary variables which are used during loading of the\n+\t * per-directory exclude lists.\n+\t *\n+\t * exclude_stack points to the top of the exclude_stack, and\n+\t * basebuf contains the full path to the current\n+\t * (sub)directory in the traversal.\n+\t */\n \tstruct exclude_stack *exclude_stack;\n \tchar basebuf[PATH_MAX];\n };\n-- \n1.7.12.147.g6d168f4\n"},{"id":"199615","messageId":"1348170383-15751-4-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 03/14] Rename cryptic 'which' variable to more consistent name","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:12Z","receivedAt":"2012-09-20T19:46:12Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"'el' is only *slightly* less cryptic, but is already used as the\nvariable name for a struct exclude_list pointer in numerous other\nplaces, so this reduces the number of cryptic variable names in use by\none :-)\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n dir.c | 10 +++++-----\n dir.h |  4 ++--\n 2 files changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 98d1995..91e57d9 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -311,7 +311,7 @@ static int no_wildcard(const char *string)\n }\n \n void add_exclude(const char *string, const char *base,\n-\t\t int baselen, struct exclude_list *which)\n+\t\t int baselen, struct exclude_list *el)\n {\n \tstruct exclude *x;\n \tsize_t len;\n@@ -346,8 +346,8 @@ void add_exclude(const char *string, const char *base,\n \tx->nowildcardlen = simple_length(string);\n \tif (*string == '*' && no_wildcard(string+1))\n \t\tx->flags |= EXC_FLAG_ENDSWITH;\n-\tALLOC_GROW(which->excludes, which->nr + 1, which->alloc);\n-\twhich->excludes[which->nr++] = x;\n+\tALLOC_GROW(el->excludes, el->nr + 1, el->alloc);\n+\tel->excludes[el->nr++] = x;\n }\n \n static void *read_skip_worktree_file_from_index(const char *path, size_t *size)\n@@ -389,7 +389,7 @@ int add_excludes_from_file_to_list(const char *fname,\n \t\t\t\t   const char *base,\n \t\t\t\t   int baselen,\n \t\t\t\t   char **buf_p,\n-\t\t\t\t   struct exclude_list *which,\n+\t\t\t\t   struct exclude_list *el,\n \t\t\t\t   int check_index)\n {\n \tstruct stat st;\n@@ -438,7 +438,7 @@ int add_excludes_from_file_to_list(const char *fname,\n \t\tif (buf[i] == '\\n') {\n \t\t\tif (entry != buf + i && entry[0] != '#') {\n \t\t\t\tbuf[i - (i && buf[i-1] == '\\r')] = 0;\n-\t\t\t\tadd_exclude(entry, base, baselen, which);\n+\t\t\t\tadd_exclude(entry, base, baselen, el);\n \t\t\t}\n \t\t\tentry = buf + i + 1;\n \t\t}\ndiff --git a/dir.h b/dir.h\nindex a226fbc..549a187 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -117,10 +117,10 @@ extern int path_excluded(struct path_exclude_check *, const char *, int namelen,\n \n \n extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,\n-\t\t\t\t\t  char **buf_p, struct exclude_list *which, int check_index);\n+\t\t\t\t\t  char **buf_p, struct exclude_list *el, int check_index);\n extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n extern void add_exclude(const char *string, const char *base,\n-\t\t\tint baselen, struct exclude_list *which);\n+\t\t\tint baselen, struct exclude_list *el);\n extern void free_excludes(struct exclude_list *el);\n extern int file_exists(const char *);\n \n-- \n1.7.12.147.g6d168f4\n"},{"id":"199614","messageId":"1348170383-15751-5-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 04/14] Rename path_excluded() to is_path_excluded()","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:13Z","receivedAt":"2012-09-20T19:46:13Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"Start adopting clearer names for exclude functions.  This 'is_*'\nnaming pattern for functions returning booleans was agreed here:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/204661/focus=204924\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n builtin/add.c      | 2 +-\n builtin/ls-files.c | 2 +-\n dir.c              | 4 ++--\n dir.h              | 2 +-\n unpack-trees.c     | 2 +-\n 5 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex e664100..075312a 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -454,7 +454,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \t\t\t    && !file_exists(pathspec[i])) {\n \t\t\t\tif (ignore_missing) {\n \t\t\t\t\tint dtype = DT_UNKNOWN;\n-\t\t\t\t\tif (path_excluded(&check, pathspec[i], -1, &dtype))\n+\t\t\t\t\tif (is_path_excluded(&check, pathspec[i], -1, &dtype))\n \t\t\t\t\t\tdir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));\n \t\t\t\t} else\n \t\t\t\t\tdie(_(\"pathspec '%s' did not match any files\"),\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex b5434af..d226456 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -203,7 +203,7 @@ static void show_ru_info(void)\n static int ce_excluded(struct path_exclude_check *check, struct cache_entry *ce)\n {\n \tint dtype = ce_to_dtype(ce);\n-\treturn path_excluded(check, ce->name, ce_namelen(ce), &dtype);\n+\treturn is_path_excluded(check, ce->name, ce_namelen(ce), &dtype);\n }\n \n static void show_files(struct dir_struct *dir)\ndiff --git a/dir.c b/dir.c\nindex 91e57d9..dad1582 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -627,8 +627,8 @@ void path_exclude_check_clear(struct path_exclude_check *check)\n  * A path to a directory known to be excluded is left in check->path to\n  * optimize for repeated checks for files in the same excluded directory.\n  */\n-int path_excluded(struct path_exclude_check *check,\n-\t\t  const char *name, int namelen, int *dtype)\n+int is_path_excluded(struct path_exclude_check *check,\n+\t\t     const char *name, int namelen, int *dtype)\n {\n \tint i;\n \tstruct strbuf *path = &check->path;\ndiff --git a/dir.h b/dir.h\nindex 549a187..41a5e32 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -113,7 +113,7 @@ struct path_exclude_check {\n };\n extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *);\n extern void path_exclude_check_clear(struct path_exclude_check *);\n-extern int path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);\n+extern int is_path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);\n \n \n extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 6d96366..724f69b 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1373,7 +1373,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,\n \t\treturn 0;\n \n \tif (o->dir &&\n-\t    path_excluded(o->path_exclude_check, name, -1, &dtype))\n+\t    is_path_excluded(o->path_exclude_check, name, -1, &dtype))\n \t\t/*\n \t\t * ce->name is explicitly excluded, so it is Ok to\n \t\t * overwrite it.\n-- \n1.7.12.147.g6d168f4\n"},{"id":"199608","messageId":"1348170383-15751-6-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 05/14] Rename excluded_from_list() to is_excluded_from_list()","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:14Z","receivedAt":"2012-09-20T19:46:14Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"Continue adopting clearer names for exclude functions.  This 'is_*'\nnaming pattern for functions returning booleans was discussed here:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/204661/focus=204924\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n dir.c          | 11 ++++++-----\n dir.h          |  4 ++--\n unpack-trees.c |  8 +++++---\n 3 files changed, 13 insertions(+), 10 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex dad1582..8f262f6 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -514,9 +514,9 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n /* Scan the list and let the last match determine the fate.\n  * Return 1 for exclude, 0 for include and -1 for undecided.\n  */\n-int excluded_from_list(const char *pathname,\n-\t\t       int pathlen, const char *basename, int *dtype,\n-\t\t       struct exclude_list *el)\n+int is_excluded_from_list(const char *pathname,\n+\t\t\t  int pathlen, const char *basename, int *dtype,\n+\t\t\t  struct exclude_list *el)\n {\n \tint i;\n \n@@ -596,8 +596,9 @@ static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n \n \tprep_exclude(dir, pathname, basename-pathname);\n \tfor (st = EXC_CMDL; st <= EXC_FILE; st++) {\n-\t\tswitch (excluded_from_list(pathname, pathlen, basename,\n-\t\t\t\t\t   dtype_p, &dir->exclude_list[st])) {\n+\t\tswitch (is_excluded_from_list(pathname, pathlen,\n+\t\t\t\t\t      basename, dtype_p,\n+\t\t\t\t\t      &dir->exclude_list[st])) {\n \t\tcase 0:\n \t\t\treturn 0;\n \t\tcase 1:\ndiff --git a/dir.h b/dir.h\nindex 41a5e32..6904a35 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -98,8 +98,8 @@ extern int within_depth(const char *name, int namelen, int depth, int max_depth)\n extern int fill_directory(struct dir_struct *dir, const char **pathspec);\n extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec);\n \n-extern int excluded_from_list(const char *pathname, int pathlen, const char *basename,\n-\t\t\t      int *dtype, struct exclude_list *el);\n+extern int is_excluded_from_list(const char *pathname, int pathlen, const char *basename,\n+\t\t\t\t int *dtype, struct exclude_list *el);\n struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len);\n \n /*\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 724f69b..56127c9 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -837,7 +837,8 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,\n {\n \tstruct cache_entry **cache_end;\n \tint dtype = DT_DIR;\n-\tint ret = excluded_from_list(prefix, prefix_len, basename, &dtype, el);\n+\tint ret = is_excluded_from_list(prefix, prefix_len,\n+\t\t\t\t\tbasename, &dtype, el);\n \n \tprefix[prefix_len++] = '/';\n \n@@ -856,7 +857,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,\n \t * with ret (iow, we know in advance the incl/excl\n \t * decision for the entire directory), clear flag here without\n \t * calling clear_ce_flags_1(). That function will call\n-\t * the expensive excluded_from_list() on every entry.\n+\t * the expensive is_excluded_from_list() on every entry.\n \t */\n \treturn clear_ce_flags_1(cache, cache_end - cache,\n \t\t\t\tprefix, prefix_len,\n@@ -939,7 +940,8 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,\n \n \t\t/* Non-directory */\n \t\tdtype = ce_to_dtype(ce);\n-\t\tret = excluded_from_list(ce->name, ce_namelen(ce), name, &dtype, el);\n+\t\tret = is_excluded_from_list(ce->name, ce_namelen(ce),\n+\t\t\t\t\t    name, &dtype, el);\n \t\tif (ret < 0)\n \t\t\tret = defval;\n \t\tif (ret > 0)\n-- \n1.7.12.147.g6d168f4\n"},{"id":"199612","messageId":"1348170383-15751-7-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 06/14] Rename excluded() to is_excluded()","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:15Z","receivedAt":"2012-09-20T19:46:15Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"Continue adopting clearer names for exclude functions.  This is_*\nnaming pattern for functions returning booleans was discussed here:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/204661/focus=204924\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n attr.c |  2 +-\n dir.c  | 10 +++++-----\n dir.h  |  4 ++--\n 3 files changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/attr.c b/attr.c\nindex 887a9ae..fd33ff9 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -270,7 +270,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,\n  * (reading the file from top to bottom), .gitattribute of the root\n  * directory (again, reading the file from top to bottom) down to the\n  * current directory, and then scan the list backwards to find the first match.\n- * This is exactly the same as what excluded() does in dir.c to deal with\n+ * This is exactly the same as what is_excluded() does in dir.c to deal with\n  * .gitignore\n  */\n \ndiff --git a/dir.c b/dir.c\nindex 8f262f6..b381b1b 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -587,7 +587,7 @@ int is_excluded_from_list(const char *pathname,\n \treturn -1; /* undecided */\n }\n \n-static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n+static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n {\n \tint pathlen = strlen(pathname);\n \tint st;\n@@ -637,7 +637,7 @@ int is_path_excluded(struct path_exclude_check *check,\n \t/*\n \t * we allow the caller to pass namelen as an optimization; it\n \t * must match the length of the name, as we eventually call\n-\t * excluded() on the whole name string.\n+\t * is_excluded() on the whole name string.\n \t */\n \tif (namelen < 0)\n \t\tnamelen = strlen(name);\n@@ -654,7 +654,7 @@ int is_path_excluded(struct path_exclude_check *check,\n \n \t\tif (ch == '/') {\n \t\t\tint dt = DT_DIR;\n-\t\t\tif (excluded(check->dir, path->buf, &dt))\n+\t\t\tif (is_excluded(check->dir, path->buf, &dt))\n \t\t\t\treturn 1;\n \t\t}\n \t\tstrbuf_addch(path, ch);\n@@ -663,7 +663,7 @@ int is_path_excluded(struct path_exclude_check *check,\n \t/* An entry in the index; cannot be a directory with subentries */\n \tstrbuf_setlen(path, 0);\n \n-\treturn excluded(check->dir, name, dtype);\n+\treturn is_excluded(check->dir, name, dtype);\n }\n \n static struct dir_entry *dir_entry_new(const char *pathname, int len)\n@@ -963,7 +963,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,\n \t\t\t\t\t  const struct path_simplify *simplify,\n \t\t\t\t\t  int dtype, struct dirent *de)\n {\n-\tint exclude = excluded(dir, path->buf, &dtype);\n+\tint exclude = is_excluded(dir, path->buf, &dtype);\n \tif (exclude && (dir->flags & DIR_COLLECT_IGNORED)\n \t    && exclude_matches_pathspec(path->buf, path->len, simplify))\n \t\tdir_add_ignored(dir, path->buf, path->len);\ndiff --git a/dir.h b/dir.h\nindex 6904a35..009c9df 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -103,8 +103,8 @@ extern int is_excluded_from_list(const char *pathname, int pathlen, const char *\n struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len);\n \n /*\n- * The excluded() API is meant for callers that check each level of leading\n- * directory hierarchies with excluded() to avoid recursing into excluded\n+ * The is_excluded() API is meant for callers that check each level of leading\n+ * directory hierarchies with is_excluded() to avoid recursing into excluded\n  * directories.  Callers that do not do so should use this API instead.\n  */\n struct path_exclude_check {\n-- \n1.7.12.147.g6d168f4\n"},{"id":"199610","messageId":"1348170383-15751-8-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 07/14] Refactor is_excluded_from_list()","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:16Z","receivedAt":"2012-09-20T19:46:16Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"The excluded function uses a new helper function called\nlast_exclude_matching_from_list() to perform the inner loop over all of\nthe exclude patterns.  The helper just tells us whether the path is\nincluded, excluded, or undecided.\n\nHowever, it may be useful to know _which_ pattern was triggered.  So\nlet's pass out the entire exclude match, which contains the status\ninformation we were already passing out.\n\nFurther patches can make use of this.\n\nThis is a modified forward port of a patch from 2009 by Jeff King:\nhttp://article.gmane.org/gmane.comp.version-control.git/108815\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n dir.c | 41 ++++++++++++++++++++++++++++++-----------\n 1 file changed, 30 insertions(+), 11 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex b381b1b..70b7d7e 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -511,22 +511,26 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \tdir->basebuf[baselen] = '\\0';\n }\n \n-/* Scan the list and let the last match determine the fate.\n- * Return 1 for exclude, 0 for include and -1 for undecided.\n+/*\n+ * Scan the given exclude list in reverse to see whether pathname\n+ * should be ignored.  The first match (i.e. the last on the list), if\n+ * any, determines the fate.  Returns the exclude_list element which\n+ * matched, or NULL for undecided.\n  */\n-int is_excluded_from_list(const char *pathname,\n-\t\t\t  int pathlen, const char *basename, int *dtype,\n-\t\t\t  struct exclude_list *el)\n+static struct exclude *last_exclude_matching_from_list(const char *pathname,\n+\t\t\t\t\t\t       int pathlen,\n+\t\t\t\t\t\t       const char *basename,\n+\t\t\t\t\t\t       int *dtype,\n+\t\t\t\t\t\t       struct exclude_list *el)\n {\n \tint i;\n \n \tif (!el->nr)\n-\t\treturn -1;\t/* undefined */\n+\t\treturn NULL;\t/* undefined */\n \n \tfor (i = el->nr - 1; 0 <= i; i--) {\n \t\tstruct exclude *x = el->excludes[i];\n \t\tconst char *name, *exclude = x->pattern;\n-\t\tint to_exclude = x->to_exclude;\n \t\tint namelen, prefix = x->nowildcardlen;\n \n \t\tif (x->flags & EXC_FLAG_MUSTBEDIR) {\n@@ -540,14 +544,14 @@ int is_excluded_from_list(const char *pathname,\n \t\t\t/* match basename */\n \t\t\tif (prefix == x->patternlen) {\n \t\t\t\tif (!strcmp_icase(exclude, basename))\n-\t\t\t\t\treturn to_exclude;\n+\t\t\t\t\treturn x;\n \t\t\t} else if (x->flags & EXC_FLAG_ENDSWITH) {\n \t\t\t\tif (x->patternlen - 1 <= pathlen &&\n \t\t\t\t    !strcmp_icase(exclude + 1, pathname + pathlen - x->patternlen + 1))\n-\t\t\t\t\treturn to_exclude;\n+\t\t\t\t\treturn x;\n \t\t\t} else {\n \t\t\t\tif (fnmatch_icase(exclude, basename, 0) == 0)\n-\t\t\t\t\treturn to_exclude;\n+\t\t\t\t\treturn x;\n \t\t\t}\n \t\t\tcontinue;\n \t\t}\n@@ -582,8 +586,23 @@ int is_excluded_from_list(const char *pathname,\n \t\t}\n \n \t\tif (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))\n-\t\t\treturn to_exclude;\n+\t\t\treturn x;\n \t}\n+\treturn NULL; /* undecided */\n+}\n+\n+/*\n+ * Scan the list and let the last match determine the fate.\n+ * Return 1 for exclude, 0 for include and -1 for undecided.\n+ */\n+int is_excluded_from_list(const char *pathname,\n+\t\t\t  int pathlen, const char *basename, int *dtype,\n+\t\t\t  struct exclude_list *el)\n+{\n+\tstruct exclude *exclude;\n+\texclude = last_exclude_matching_from_list(pathname, pathlen, basename, dtype, el);\n+\tif (exclude)\n+\t\treturn exclude->to_exclude;\n \treturn -1; /* undecided */\n }\n \n-- \n1.7.12.147.g6d168f4\n"},{"id":"199619","messageId":"1348170383-15751-9-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 08/14] Refactor is_excluded()","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:17Z","receivedAt":"2012-09-20T19:46:17Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"In a similar way to the previous commit, this extracts a new helper\nfunction last_exclude_matching() which returns the last exclude_list\nelement which matched, or NULL if no match was found.  is_excluded()\nbecomes a wrapper around this, and just returns 0 or 1 depending on\nwhether any matching exclude_list element was found.\n\nThis allows callers to find out _why_ a given path was excluded,\nrather than just whether it was or not, paving the way for a new git\nsub-command which allows users to test their exclude lists from the\ncommand line.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n dir.c | 38 +++++++++++++++++++++++++++++---------\n 1 file changed, 29 insertions(+), 9 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 70b7d7e..3e2161e 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -606,24 +606,44 @@ int is_excluded_from_list(const char *pathname,\n \treturn -1; /* undecided */\n }\n \n-static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n+/*\n+ * Loads the exclude lists for the directory containing pathname, then\n+ * scans all exclude lists to determine whether pathname is excluded.\n+ * Returns the exclude_list element which matched, or NULL for\n+ * undecided.\n+ */\n+static struct exclude *last_exclude_matching(struct dir_struct *dir,\n+\t\t\t\t\t     const char *pathname,\n+\t\t\t\t\t     int *dtype_p)\n {\n \tint pathlen = strlen(pathname);\n \tint st;\n+\tstruct exclude *exclude;\n \tconst char *basename = strrchr(pathname, '/');\n \tbasename = (basename) ? basename+1 : pathname;\n \n \tprep_exclude(dir, pathname, basename-pathname);\n \tfor (st = EXC_CMDL; st <= EXC_FILE; st++) {\n-\t\tswitch (is_excluded_from_list(pathname, pathlen,\n-\t\t\t\t\t      basename, dtype_p,\n-\t\t\t\t\t      &dir->exclude_list[st])) {\n-\t\tcase 0:\n-\t\t\treturn 0;\n-\t\tcase 1:\n-\t\t\treturn 1;\n-\t\t}\n+\t\texclude = last_exclude_matching_from_list(\n+\t\t\tpathname, pathlen, basename, dtype_p,\n+\t\t\t&dir->exclude_list[st]);\n+\t\tif (exclude)\n+\t\t\treturn exclude;\n \t}\n+\treturn NULL;\n+}\n+\n+/*\n+ * Loads the exclude lists for the directory containing pathname, then\n+ * scans all exclude lists to determine whether pathname is excluded.\n+ * Returns 1 if true, otherwise 0.\n+ */\n+static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n+{\n+\tstruct exclude *exclude =\n+\t\tlast_exclude_matching(dir, pathname, dtype_p);\n+\tif (exclude)\n+\t\treturn exclude->to_exclude;\n \treturn 0;\n }\n \n-- \n1.7.12.147.g6d168f4\n"},{"id":"199618","messageId":"1348170383-15751-10-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 09/14] Refactor is_path_excluded()","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:18Z","receivedAt":"2012-09-20T19:46:18Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"In a similar way to the previous commit, this extracts a new helper\nfunction last_exclude_matching_path() which return the last\nexclude_list element which matched, or NULL if no match was found.\nis_path_excluded() becomes a wrapper around this, and just returns 0\nor 1 depending on whether any matching exclude_list element was found.\n\nThis allows callers to find out _why_ a given path was excluded,\nrather than just whether it was or not, paving the way for a new git\nsub-command which allows users to test their exclude lists from the\ncommand line.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n dir.c | 47 ++++++++++++++++++++++++++++++++++++++---------\n dir.h |  3 +++\n 2 files changed, 41 insertions(+), 9 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 3e2161e..904a7f3 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -651,6 +651,7 @@ void path_exclude_check_init(struct path_exclude_check *check,\n \t\t\t     struct dir_struct *dir)\n {\n \tcheck->dir = dir;\n+\tcheck->exclude = NULL;\n \tstrbuf_init(&check->path, 256);\n }\n \n@@ -660,18 +661,21 @@ void path_exclude_check_clear(struct path_exclude_check *check)\n }\n \n /*\n- * Is this name excluded?  This is for a caller like show_files() that\n- * do not honor directory hierarchy and iterate through paths that are\n- * possibly in an ignored directory.\n+ * For each subdirectory in name, starting with the top-most, checks\n+ * to see if that subdirectory is excluded, and if so, returns the\n+ * corresponding exclude structure.  Otherwise, checks whether name\n+ * itself (which is presumably a file) is excluded.\n  *\n  * A path to a directory known to be excluded is left in check->path to\n  * optimize for repeated checks for files in the same excluded directory.\n  */\n-int is_path_excluded(struct path_exclude_check *check,\n-\t\t     const char *name, int namelen, int *dtype)\n+struct exclude *last_exclude_matching_path(struct path_exclude_check *check,\n+\t\t\t\t\t   const char *name, int namelen,\n+\t\t\t\t\t   int *dtype)\n {\n \tint i;\n \tstruct strbuf *path = &check->path;\n+\tstruct exclude *exclude;\n \n \t/*\n \t * we allow the caller to pass namelen as an optimization; it\n@@ -681,11 +685,17 @@ int is_path_excluded(struct path_exclude_check *check,\n \tif (namelen < 0)\n \t\tnamelen = strlen(name);\n \n+\t/*\n+\t * If path is non-empty, and name is equal to path or a\n+\t * subdirectory of path, name should be excluded, because\n+\t * it's inside a directory which is already known to be\n+\t * excluded and was previously left in check->path.\n+\t */\n \tif (path->len &&\n \t    path->len <= namelen &&\n \t    !memcmp(name, path->buf, path->len) &&\n \t    (!name[path->len] || name[path->len] == '/'))\n-\t\treturn 1;\n+\t\treturn check->exclude;\n \n \tstrbuf_setlen(path, 0);\n \tfor (i = 0; name[i]; i++) {\n@@ -693,8 +703,12 @@ int is_path_excluded(struct path_exclude_check *check,\n \n \t\tif (ch == '/') {\n \t\t\tint dt = DT_DIR;\n-\t\t\tif (is_excluded(check->dir, path->buf, &dt))\n-\t\t\t\treturn 1;\n+\t\t\texclude = last_exclude_matching(check->dir,\n+\t\t\t\t\t\t\tpath->buf, &dt);\n+\t\t\tif (exclude) {\n+\t\t\t\tcheck->exclude = exclude;\n+\t\t\t\treturn exclude;\n+\t\t\t}\n \t\t}\n \t\tstrbuf_addch(path, ch);\n \t}\n@@ -702,7 +716,22 @@ int is_path_excluded(struct path_exclude_check *check,\n \t/* An entry in the index; cannot be a directory with subentries */\n \tstrbuf_setlen(path, 0);\n \n-\treturn is_excluded(check->dir, name, dtype);\n+\treturn last_exclude_matching(check->dir, name, dtype);\n+}\n+\n+/*\n+ * Is this name excluded?  This is for a caller like show_files() that\n+ * do not honor directory hierarchy and iterate through paths that are\n+ * possibly in an ignored directory.\n+ */\n+int is_path_excluded(struct path_exclude_check *check,\n+\t\t  const char *name, int namelen, int *dtype)\n+{\n+\tstruct exclude *exclude =\n+\t\tlast_exclude_matching_path(check, name, namelen, dtype);\n+\tif (exclude)\n+\t\treturn exclude->to_exclude;\n+\treturn 0;\n }\n \n static struct dir_entry *dir_entry_new(const char *pathname, int len)\ndiff --git a/dir.h b/dir.h\nindex 009c9df..19beddb 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -109,10 +109,13 @@ struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname,\n  */\n struct path_exclude_check {\n \tstruct dir_struct *dir;\n+\tstruct exclude *exclude;\n \tstruct strbuf path;\n };\n extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *);\n extern void path_exclude_check_clear(struct path_exclude_check *);\n+extern struct exclude *last_exclude_matching_path(struct path_exclude_check *, const char *,\n+\t\t\t\t\t\t  int namelen, int *dtype);\n extern int is_path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);\n \n \n-- \n1.7.12.147.g6d168f4\n"},{"id":"199617","messageId":"1348170383-15751-11-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 10/14] For each exclude pattern, store information about where it came from","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:19Z","receivedAt":"2012-09-20T19:46:19Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"For exclude patterns read in from files, the filename is stored together\nwith the corresponding line number (counting starting at 1).\n\nFor exclude patterns provided on the command line, the sequence number\nis negative, with counting starting at -1, so for example the 2nd\npattern provided via --exclude would be numbered -2.  This allows any\nfuture consumers of that data to easily distinguish between exclude\npatterns from files vs. from the CLI.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n builtin/clean.c    |  2 +-\n builtin/ls-files.c |  3 ++-\n dir.c              | 25 +++++++++++++++++++------\n dir.h              |  5 ++++-\n 4 files changed, 26 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/clean.c b/builtin/clean.c\nindex 69c1cda..893dd7a 100644\n--- a/builtin/clean.c\n+++ b/builtin/clean.c\n@@ -99,7 +99,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)\n \n \tfor (i = 0; i < exclude_list.nr; i++)\n \t\tadd_exclude(exclude_list.items[i].string, \"\", 0,\n-\t\t\t    &dir.exclude_list[EXC_CMDL]);\n+\t\t\t    &dir.exclude_list[EXC_CMDL], \"--exclude option\", -(i+1));\n \n \tpathspec = get_pathspec(prefix, argv);\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex d226456..64b1969 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -35,6 +35,7 @@ static int error_unmatch;\n static char *ps_matched;\n static const char *with_tree;\n static int exc_given;\n+static int exclude_args;\n \n static const char *tag_cached = \"\";\n static const char *tag_unmerged = \"\";\n@@ -423,7 +424,7 @@ static int option_parse_exclude(const struct option *opt,\n \tstruct exclude_list *list = opt->value;\n \n \texc_given = 1;\n-\tadd_exclude(arg, \"\", 0, list);\n+\tadd_exclude(arg, \"\", 0, list, \"--exclude option\", --exclude_args);\n \n \treturn 0;\n }\ndiff --git a/dir.c b/dir.c\nindex 904a7f3..705546f 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -311,7 +311,7 @@ static int no_wildcard(const char *string)\n }\n \n void add_exclude(const char *string, const char *base,\n-\t\t int baselen, struct exclude_list *el)\n+\t\t int baselen, struct exclude_list *el, const char *src, int srcpos)\n {\n \tstruct exclude *x;\n \tsize_t len;\n@@ -341,6 +341,8 @@ void add_exclude(const char *string, const char *base,\n \tx->base = base;\n \tx->baselen = baselen;\n \tx->flags = flags;\n+\tx->src = src;\n+\tx->srcpos = srcpos;\n \tif (!strchr(string, '/'))\n \t\tx->flags |= EXC_FLAG_NODIR;\n \tx->nowildcardlen = simple_length(string);\n@@ -393,7 +395,7 @@ int add_excludes_from_file_to_list(const char *fname,\n \t\t\t\t   int check_index)\n {\n \tstruct stat st;\n-\tint fd, i;\n+\tint fd, i, lineno = 1;\n \tsize_t size = 0;\n \tchar *buf, *entry;\n \n@@ -438,8 +440,9 @@ int add_excludes_from_file_to_list(const char *fname,\n \t\tif (buf[i] == '\\n') {\n \t\t\tif (entry != buf + i && entry[0] != '#') {\n \t\t\t\tbuf[i - (i && buf[i-1] == '\\r')] = 0;\n-\t\t\t\tadd_exclude(entry, base, baselen, el);\n+\t\t\t\tadd_exclude(entry, base, baselen, el, fname, lineno);\n \t\t\t}\n+\t\t\tlineno++;\n \t\t\tentry = buf + i + 1;\n \t\t}\n \t}\n@@ -474,8 +477,10 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t\t    !strncmp(dir->basebuf, base, stk->baselen))\n \t\t\tbreak;\n \t\tdir->exclude_stack = stk->prev;\n-\t\twhile (stk->exclude_ix < el->nr)\n-\t\t\tfree(el->excludes[--el->nr]);\n+\t\twhile (stk->exclude_ix < el->nr) {\n+\t\t\tstruct exclude *exclude = el->excludes[--el->nr];\n+\t\t\tfree(exclude);\n+\t\t}\n \t\tfree(stk->filebuf);\n \t\tfree(stk);\n \t}\n@@ -502,7 +507,15 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t\tmemcpy(dir->basebuf + current, base + current,\n \t\t       stk->baselen - current);\n \t\tstrcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);\n-\t\tadd_excludes_from_file_to_list(dir->basebuf,\n+\n+\t\t/* dir->basebuf gets reused by the traversal, but we\n+\t\t * need fname to remain unchanged to ensure the src\n+\t\t * member of each struct exclude correctly back-references\n+\t\t * its source file.\n+\t\t */\n+\t\tchar *fname = strdup(dir->basebuf);\n+\n+\t\tadd_excludes_from_file_to_list(fname,\n \t\t\t\t\t       dir->basebuf, stk->baselen,\n \t\t\t\t\t       &stk->filebuf, el, 1);\n \t\tdir->exclude_stack = stk;\ndiff --git a/dir.h b/dir.h\nindex 19beddb..ebb0367 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -31,6 +31,9 @@ struct exclude_list {\n \t\tint baselen;\n \t\tint to_exclude;\n \t\tint flags;\n+\t\tconst char *src;\n+\t\tint srcpos; /* counting starts from 1 for line numbers in ignore files,\n+\t\t\t       and from -1 decrementing for patterns from CLI (--exclude) */\n \t} **excludes;\n };\n \n@@ -123,7 +126,7 @@ extern int add_excludes_from_file_to_list(const char *fname, const char *base, i\n \t\t\t\t\t  char **buf_p, struct exclude_list *el, int check_index);\n extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n extern void add_exclude(const char *string, const char *base,\n-\t\t\tint baselen, struct exclude_list *el);\n+\t\t\tint baselen, struct exclude_list *el, const char *src, int srcpos);\n extern void free_excludes(struct exclude_list *el);\n extern int file_exists(const char *);\n \n-- \n1.7.12.147.g6d168f4\n"},{"id":"199616","messageId":"1348170383-15751-12-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 11/14] Refactor treat_gitlinks()","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:20Z","receivedAt":"2012-09-20T19:46:20Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"Extract the body of the for loop in treat_gitlinks() into a separate\ntreat_gitlink() function so that it can be reused elsewhere.  This\npaves the way for a new check-ignore sub-command.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n builtin/add.c | 49 +++++++++++++++++++++++++++++++------------------\n 1 file changed, 31 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 075312a..b4ec5cd 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -153,31 +153,44 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, int\n \treturn seen;\n }\n \n-static void treat_gitlinks(const char **pathspec)\n+/*\n+ * Check whether path refers to a submodule, or something inside a\n+ * submodule.  If the former, returns the path with any trailing slash\n+ * stripped.  If the latter, dies with an error message.\n+ */\n+const char *treat_gitlink(const char *path)\n {\n-\tint i;\n-\n-\tif (!pathspec || !*pathspec)\n-\t\treturn;\n-\n+\tint i, path_len = strlen(path);\n \tfor (i = 0; i < active_nr; i++) {\n \t\tstruct cache_entry *ce = active_cache[i];\n \t\tif (S_ISGITLINK(ce->ce_mode)) {\n-\t\t\tint len = ce_namelen(ce), j;\n-\t\t\tfor (j = 0; pathspec[j]; j++) {\n-\t\t\t\tint len2 = strlen(pathspec[j]);\n-\t\t\t\tif (len2 <= len || pathspec[j][len] != '/' ||\n-\t\t\t\t    memcmp(ce->name, pathspec[j], len))\n-\t\t\t\t\tcontinue;\n-\t\t\t\tif (len2 == len + 1)\n-\t\t\t\t\t/* strip trailing slash */\n-\t\t\t\t\tpathspec[j] = xstrndup(ce->name, len);\n-\t\t\t\telse\n-\t\t\t\t\tdie (_(\"Path '%s' is in submodule '%.*s'\"),\n-\t\t\t\t\t\tpathspec[j], len, ce->name);\n+\t\t\tint ce_len = ce_namelen(ce);\n+\t\t\tif (path_len <= ce_len || path[ce_len] != '/' ||\n+\t\t\t    memcmp(ce->name, path, ce_len))\n+\t\t\t\t/* path does not refer to this\n+\t\t\t\t * submodule or anything inside it */\n+\t\t\t\tcontinue;\n+\t\t\tif (path_len == ce_len + 1) {\n+\t\t\t\t/* path refers to submodule;\n+\t\t\t\t * strip trailing slash */\n+\t\t\t\treturn xstrndup(ce->name, ce_len);\n+\t\t\t} else {\n+\t\t\t\tdie (_(\"Path '%s' is in submodule '%.*s'\"),\n+\t\t\t\t     path, ce_len, ce->name);\n \t\t\t}\n \t\t}\n \t}\n+\treturn path;\n+}\n+\n+void treat_gitlinks(const char **pathspec)\n+{\n+\tif (!pathspec || !*pathspec)\n+\t\treturn;\n+\n+\tint i;\n+\tfor (i = 0; pathspec[i]; i++)\n+\t\tpathspec[i] = treat_gitlink(pathspec[i]);\n }\n \n static void refresh(int verbose, const char **pathspec)\n-- \n1.7.12.147.g6d168f4\n"},{"id":"199613","messageId":"1348170383-15751-13-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 12/14] Extract some useful pathspec handling code from builtin/add.c into a library","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:21Z","receivedAt":"2012-09-20T19:46:21Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"This is in preparation for reuse by a new git check-ignore command.\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n Makefile      |  2 ++\n builtin/add.c | 95 ++-------------------------------------------------------\n pathspec.c    | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n pathspec.h    |  6 ++++\n 4 files changed, 108 insertions(+), 92 deletions(-)\n create mode 100644 pathspec.c\n create mode 100644 pathspec.h\n\ndiff --git a/Makefile b/Makefile\nindex a49d1db..9a4bf9e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -649,6 +649,7 @@ LIB_H += pack-revindex.h\n LIB_H += pack.h\n LIB_H += parse-options.h\n LIB_H += patch-ids.h\n+LIB_H += pathspec.h\n LIB_H += pkt-line.h\n LIB_H += progress.h\n LIB_H += prompt.h\n@@ -769,6 +770,7 @@ LIB_OBJS += parse-options-cb.o\n LIB_OBJS += patch-delta.o\n LIB_OBJS += patch-ids.o\n LIB_OBJS += path.o\n+LIB_OBJS += pathspec.o\n LIB_OBJS += pkt-line.o\n LIB_OBJS += preload-index.o\n LIB_OBJS += pretty.o\ndiff --git a/builtin/add.c b/builtin/add.c\nindex b4ec5cd..c3def9c 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -6,6 +6,7 @@\n #include \"cache.h\"\n #include \"builtin.h\"\n #include \"dir.h\"\n+#include \"pathspec.h\"\n #include \"exec_cmd.h\"\n #include \"cache-tree.h\"\n #include \"run-command.h\"\n@@ -97,39 +98,6 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)\n \treturn !!data.add_errors;\n }\n \n-static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)\n-{\n-\tint num_unmatched = 0, i;\n-\n-\t/*\n-\t * Since we are walking the index as if we were walking the directory,\n-\t * we have to mark the matched pathspec as seen; otherwise we will\n-\t * mistakenly think that the user gave a pathspec that did not match\n-\t * anything.\n-\t */\n-\tfor (i = 0; i < specs; i++)\n-\t\tif (!seen[i])\n-\t\t\tnum_unmatched++;\n-\tif (!num_unmatched)\n-\t\treturn;\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tstruct cache_entry *ce = active_cache[i];\n-\t\tmatch_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);\n-\t}\n-}\n-\n-static char *find_used_pathspec(const char **pathspec)\n-{\n-\tchar *seen;\n-\tint i;\n-\n-\tfor (i = 0; pathspec[i];  i++)\n-\t\t; /* just counting */\n-\tseen = xcalloc(i, 1);\n-\tfill_pathspec_matches(pathspec, seen, i);\n-\treturn seen;\n-}\n-\n static char *prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)\n {\n \tchar *seen;\n@@ -153,46 +121,6 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, int\n \treturn seen;\n }\n \n-/*\n- * Check whether path refers to a submodule, or something inside a\n- * submodule.  If the former, returns the path with any trailing slash\n- * stripped.  If the latter, dies with an error message.\n- */\n-const char *treat_gitlink(const char *path)\n-{\n-\tint i, path_len = strlen(path);\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tstruct cache_entry *ce = active_cache[i];\n-\t\tif (S_ISGITLINK(ce->ce_mode)) {\n-\t\t\tint ce_len = ce_namelen(ce);\n-\t\t\tif (path_len <= ce_len || path[ce_len] != '/' ||\n-\t\t\t    memcmp(ce->name, path, ce_len))\n-\t\t\t\t/* path does not refer to this\n-\t\t\t\t * submodule or anything inside it */\n-\t\t\t\tcontinue;\n-\t\t\tif (path_len == ce_len + 1) {\n-\t\t\t\t/* path refers to submodule;\n-\t\t\t\t * strip trailing slash */\n-\t\t\t\treturn xstrndup(ce->name, ce_len);\n-\t\t\t} else {\n-\t\t\t\tdie (_(\"Path '%s' is in submodule '%.*s'\"),\n-\t\t\t\t     path, ce_len, ce->name);\n-\t\t\t}\n-\t\t}\n-\t}\n-\treturn path;\n-}\n-\n-void treat_gitlinks(const char **pathspec)\n-{\n-\tif (!pathspec || !*pathspec)\n-\t\treturn;\n-\n-\tint i;\n-\tfor (i = 0; pathspec[i]; i++)\n-\t\tpathspec[i] = treat_gitlink(pathspec[i]);\n-}\n-\n static void refresh(int verbose, const char **pathspec)\n {\n \tchar *seen;\n@@ -210,23 +138,6 @@ static void refresh(int verbose, const char **pathspec)\n         free(seen);\n }\n \n-static const char **validate_pathspec(int argc, const char **argv, const char *prefix)\n-{\n-\tconst char **pathspec = get_pathspec(prefix, argv);\n-\n-\tif (pathspec) {\n-\t\tconst char **p;\n-\t\tfor (p = pathspec; *p; p++) {\n-\t\t\tif (has_symlink_leading_path(*p, strlen(*p))) {\n-\t\t\t\tint len = prefix ? strlen(prefix) : 0;\n-\t\t\t\tdie(_(\"'%s' is beyond a symbolic link\"), *p + len);\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\treturn pathspec;\n-}\n-\n int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\tconst char **pathspec)\n {\n@@ -261,7 +172,7 @@ int interactive_add(int argc, const char **argv, const char *prefix, int patch)\n \tconst char **pathspec = NULL;\n \n \tif (argc) {\n-\t\tpathspec = validate_pathspec(argc, argv, prefix);\n+\t\tpathspec = validate_pathspec(prefix, argv);\n \t\tif (!pathspec)\n \t\t\treturn -1;\n \t}\n@@ -428,7 +339,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \t\tfprintf(stderr, _(\"Maybe you wanted to say 'git add .'?\\n\"));\n \t\treturn 0;\n \t}\n-\tpathspec = validate_pathspec(argc, argv, prefix);\n+\tpathspec = validate_pathspec(prefix, argv);\n \n \tif (read_cache() < 0)\n \t\tdie(_(\"index file corrupt\"));\ndiff --git a/pathspec.c b/pathspec.c\nnew file mode 100644\nindex 0000000..10f6643\n--- /dev/null\n+++ b/pathspec.c\n@@ -0,0 +1,97 @@\n+#include \"cache.h\"\n+#include \"dir.h\"\n+\n+void validate_path(const char *prefix, const char *path)\n+{\n+\tif (has_symlink_leading_path(path, strlen(path))) {\n+\t\tint len = prefix ? strlen(prefix) : 0;\n+\t\tdie(_(\"'%s' is beyond a symbolic link\"), path + len);\n+\t}\n+}\n+\n+const char **validate_pathspec(const char *prefix, const char **files)\n+{\n+\tconst char **pathspec = get_pathspec(prefix, files);\n+\n+\tif (pathspec) {\n+\t\tconst char **p;\n+\t\tfor (p = pathspec; *p; p++) {\n+\t\t\tvalidate_path(prefix, *p);\n+\t\t}\n+\t}\n+\n+\treturn pathspec;\n+}\n+\n+void fill_pathspec_matches(const char **pathspec, char *seen, int specs)\n+{\n+\tint num_unmatched = 0, i;\n+\n+\t/*\n+\t * Since we are walking the index as if we were walking the directory,\n+\t * we have to mark the matched pathspec as seen; otherwise we will\n+\t * mistakenly think that the user gave a pathspec that did not match\n+\t * anything.\n+\t */\n+\tfor (i = 0; i < specs; i++)\n+\t\tif (!seen[i])\n+\t\t\tnum_unmatched++;\n+\tif (!num_unmatched)\n+\t\treturn;\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tmatch_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);\n+\t}\n+}\n+\n+char *find_used_pathspec(const char **pathspec)\n+{\n+\tchar *seen;\n+\tint i;\n+\n+\tfor (i = 0; pathspec[i];  i++)\n+\t\t; /* just counting */\n+\tseen = xcalloc(i, 1);\n+\tfill_pathspec_matches(pathspec, seen, i);\n+\treturn seen;\n+}\n+\n+/*\n+ * Check whether path refers to a submodule, or something inside a\n+ * submodule.  If the former, returns the path with any trailing slash\n+ * stripped.  If the latter, dies with an error message.\n+ */\n+const char *treat_gitlink(const char *path)\n+{\n+\tint i, path_len = strlen(path);\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tif (S_ISGITLINK(ce->ce_mode)) {\n+\t\t\tint ce_len = ce_namelen(ce);\n+\t\t\tif (path_len <= ce_len || path[ce_len] != '/' ||\n+\t\t\t    memcmp(ce->name, path, ce_len))\n+\t\t\t\t/* path does not refer to this\n+\t\t\t\t * submodule or anything inside it */\n+\t\t\t\tcontinue;\n+\t\t\tif (path_len == ce_len + 1) {\n+\t\t\t\t/* path refers to submodule;\n+\t\t\t\t * strip trailing slash */\n+\t\t\t\treturn xstrndup(ce->name, ce_len);\n+\t\t\t} else {\n+\t\t\t\tdie (_(\"Path '%s' is in submodule '%.*s'\"),\n+\t\t\t\t     path, ce_len, ce->name);\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn path;\n+}\n+\n+void treat_gitlinks(const char **pathspec)\n+{\n+\tif (!pathspec || !*pathspec)\n+\t\treturn;\n+\n+\tint i;\n+\tfor (i = 0; pathspec[i]; i++)\n+\t\tpathspec[i] = treat_gitlink(pathspec[i]);\n+}\ndiff --git a/pathspec.h b/pathspec.h\nnew file mode 100644\nindex 0000000..4ed40a5\n--- /dev/null\n+++ b/pathspec.h\n@@ -0,0 +1,6 @@\n+extern void validate_path(const char *prefix, const char *path);\n+extern const char **validate_pathspec(const char *prefix, const char **files);\n+extern char *find_used_pathspec(const char **pathspec);\n+extern void fill_pathspec_matches(const char **pathspec, char *seen, int specs);\n+extern const char *treat_gitlink(const char **path);\n+extern void treat_gitlinks(const char **pathspec);\n-- \n1.7.12.147.g6d168f4\n"},{"id":"199611","messageId":"1348170383-15751-14-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 13/14] Provide free_directory() for reclaiming dir_struct memory","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:22Z","receivedAt":"2012-09-20T19:46:22Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"Signed-off-by: Adam Spiers <git@adamspiers.org>\n---\n Documentation/technical/api-directory-listing.txt |  2 ++\n dir.c                                             | 23 +++++++++++++++++++++--\n dir.h                                             |  1 +\n 3 files changed, 24 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex 944fc39..e339c18 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -79,4 +79,6 @@ marked. If you to exclude files, make sure you have loaded index first.\n \n * Use `dir.entries[]`.\n \n+* Call `free_directory()` when none of the contained elements are no longer in use.\n+\n (JC)\ndiff --git a/dir.c b/dir.c\nindex 705546f..a04739c 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -456,6 +456,12 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)\n \t\tdie(\"cannot use %s as an exclude file\", fname);\n }\n \n+static void free_exclude_stack(struct exclude_stack *stk)\n+{\n+\tfree(stk->filebuf);\n+\tfree(stk);\n+}\n+\n /*\n  * Loads the per-directory exclude list for the substring of base\n  * which has a char length of baselen.\n@@ -481,8 +487,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t\t\tstruct exclude *exclude = el->excludes[--el->nr];\n \t\t\tfree(exclude);\n \t\t}\n-\t\tfree(stk->filebuf);\n-\t\tfree(stk);\n+\t\tfree_exclude_stack(stk);\n \t}\n \n \t/* Read from the parent directories and push them down. */\n@@ -1473,3 +1478,17 @@ void free_pathspec(struct pathspec *pathspec)\n \tfree(pathspec->items);\n \tpathspec->items = NULL;\n }\n+\n+void free_directory(struct dir_struct *dir)\n+{\n+\tint st;\n+\tfor (st = EXC_CMDL; st <= EXC_FILE; st++)\n+\t\tfree_excludes(&dir->exclude_list[st]);\n+\n+\tstruct exclude_stack *prev, *stk = dir->exclude_stack;\n+\twhile (stk) {\n+\t\tprev = stk->prev;\n+\t\tfree_exclude_stack(stk);\n+\t\tstk = prev;\n+\t}\n+}\ndiff --git a/dir.h b/dir.h\nindex ebb0367..7da29da 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -128,6 +128,7 @@ extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n extern void add_exclude(const char *string, const char *base,\n \t\t\tint baselen, struct exclude_list *el, const char *src, int srcpos);\n extern void free_excludes(struct exclude_list *el);\n+extern void free_directory(struct dir_struct *dir);\n extern int file_exists(const char *);\n \n extern int is_inside_dir(const char *dir);\n-- \n1.7.12.147.g6d168f4\n"},{"id":"199620","messageId":"1348170383-15751-15-git-send-email-git@adamspiers.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"[PATCH v2 14/14] Add git-check-ignore sub-command","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T19:46:23Z","receivedAt":"2012-09-20T19:46:23Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"This works in a similar manner to git-check-attr.  Some code\nwas reused from add.c by refactoring out into pathspec.c.\n\nThanks to Jeff King and Junio C Hamano for the idea:\nhttp://thread.gmane.org/gmane.comp.version-control.git/108671/focus=108815\n\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n .gitignore                             |   1 +\n Documentation/git-check-ignore.txt     |  85 +++++\n Documentation/gitignore.txt            |   6 +-\n Makefile                               |   1 +\n builtin.h                              |   1 +\n builtin/check-ignore.c                 | 167 ++++++++++\n command-list.txt                       |   1 +\n contrib/completion/git-completion.bash |   1 +\n git.c                                  |   1 +\n t/t0007-ignores.sh                     | 587 +++++++++++++++++++++++++++++++++\n t/t9902-completion.sh                  |  24 +-\n 11 files changed, 861 insertions(+), 14 deletions(-)\n create mode 100644 Documentation/git-check-ignore.txt\n create mode 100644 builtin/check-ignore.c\n create mode 100755 t/t0007-ignores.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex a188a82..11cd975 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -22,6 +22,7 @@\n /git-bundle\n /git-cat-file\n /git-check-attr\n+/git-check-ignore\n /git-check-ref-format\n /git-checkout\n /git-checkout-index\ndiff --git a/Documentation/git-check-ignore.txt b/Documentation/git-check-ignore.txt\nnew file mode 100644\nindex 0000000..2de4e17\n--- /dev/null\n+++ b/Documentation/git-check-ignore.txt\n@@ -0,0 +1,85 @@\n+git-check-ignore(1)\n+=================\n+\n+NAME\n+----\n+git-check-ignore - Debug gitignore / exclude files\n+\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git check-ignore' [options] pathname...\n+'git check-ignore' [options] --stdin < <list-of-paths>\n+\n+DESCRIPTION\n+-----------\n+\n+For each pathname given via the command-line or from a file via\n+`--stdin`, this command will list the first exclude pattern found (if\n+any) which explicitly excludes or includes that pathname.  Note that\n+within any given exclude file, later patterns take precedence over\n+earlier ones, so any matching pattern which this command outputs may\n+not be the one you would immediately expect.\n+\n+OPTIONS\n+-------\n+-q, --quiet::\n+\tDon't output anything, just set exit status.  This is only\n+\tvalid with a single pathname.\n+\n+-v, --verbose::\n+\tAlso output details about the matching pattern (if any)\n+\tfor each given pathname.\n+\n+--stdin::\n+\tRead file names from stdin instead of from the command-line.\n+\n+-z::\n+\tThe output format is modified to be machine-parseable (see\n+\tbelow).  If `--stdin` is also given, input paths are separated\n+\twith a NUL character instead of a linefeed character.\n+\n+OUTPUT\n+------\n+\n+By default, any of the given pathnames which match an ignore pattern\n+will be output, one per line.  If no pattern matches a given path,\n+nothing will be output for that path; this means that path will not be\n+ignored.\n+\n+If `--verbose` is specified, the output is a series of lines of the form:\n+\n+<source> <COLON> <linenum> <COLON> <pattern> <HT> <pathname>\n+\n+<pathname> is the path of a file being queried, <pattern> is the\n+matching pattern, <source> is the pattern's source file, and <linenum>\n+is the line number of the pattern within that source.  If the pattern\n+contained a `!` prefix or `/` suffix, it will be preserved in the\n+output.  <source> will be an absolute path when referring to the file\n+configured by `core.excludesfile`, or relative to the repository root\n+when referring to `.git/info/exclude` or a per-directory exclude file.\n+\n+If `-z` is specified, the output is a series of lines of the form:\n+\n+EXIT STATUS\n+-----------\n+\n+0::\n+\tOne or more of the provided paths is ignored.\n+\n+1::\n+\tNone of the provided paths are ignored.\n+\n+128::\n+\tA fatal error was encountered.\n+\n+SEE ALSO\n+--------\n+linkgit:gitignore[5]\n+linkgit:gitconfig[5]\n+linkgit:git-ls-files[5]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt\nindex c1f692a..7ba16fe 100644\n--- a/Documentation/gitignore.txt\n+++ b/Documentation/gitignore.txt\n@@ -155,8 +155,10 @@ The second .gitignore prevents git from ignoring\n \n SEE ALSO\n --------\n-linkgit:git-rm[1], linkgit:git-update-index[1],\n-linkgit:gitrepository-layout[5]\n+linkgit:git-rm[1],\n+linkgit:git-update-index[1],\n+linkgit:gitrepository-layout[5],\n+linkgit:git-check-ignore[1]\n \n GIT\n ---\ndiff --git a/Makefile b/Makefile\nindex 9a4bf9e..2d78b83 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -834,6 +834,7 @@ BUILTIN_OBJS += builtin/branch.o\n BUILTIN_OBJS += builtin/bundle.o\n BUILTIN_OBJS += builtin/cat-file.o\n BUILTIN_OBJS += builtin/check-attr.o\n+BUILTIN_OBJS += builtin/check-ignore.o\n BUILTIN_OBJS += builtin/check-ref-format.o\n BUILTIN_OBJS += builtin/checkout-index.o\n BUILTIN_OBJS += builtin/checkout.o\ndiff --git a/builtin.h b/builtin.h\nindex 95116b8..7519f81 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -55,6 +55,7 @@ extern int cmd_cat_file(int argc, const char **argv, const char *prefix);\n extern int cmd_checkout(int argc, const char **argv, const char *prefix);\n extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);\n extern int cmd_check_attr(int argc, const char **argv, const char *prefix);\n+extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);\n extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/check-ignore.c b/builtin/check-ignore.c\nnew file mode 100644\nindex 0000000..74ea2fc\n--- /dev/null\n+++ b/builtin/check-ignore.c\n@@ -0,0 +1,167 @@\n+#include \"builtin.h\"\n+#include \"cache.h\"\n+#include \"dir.h\"\n+#include \"quote.h\"\n+#include \"pathspec.h\"\n+#include \"parse-options.h\"\n+\n+static int quiet, verbose, stdin_paths;\n+static const char * const check_ignore_usage[] = {\n+\"git check-ignore [options] pathname...\",\n+\"git check-ignore [options] --stdin < <list-of-paths>\",\n+NULL\n+};\n+\n+static int null_term_line;\n+\n+static const struct option check_ignore_options[] = {\n+\tOPT__QUIET(&quiet, N_(\"suppress progress reporting\")),\n+\tOPT__VERBOSE(&verbose, N_(\"be verbose\")),\n+\tOPT_GROUP(\"\"),\n+\tOPT_BOOLEAN(0, \"stdin\", &stdin_paths,\n+\t\t    N_(\"read file names from stdin\")),\n+\tOPT_BOOLEAN('z', NULL, &null_term_line,\n+\t\t    N_(\"input paths are terminated by a null character\")),\n+\tOPT_END()\n+};\n+\n+static void output_exclude(const char *path, struct exclude *exclude)\n+{\n+\tchar *bang = exclude->to_exclude ? \"\" : \"!\";\n+\tchar *dir  = (exclude->flags & EXC_FLAG_MUSTBEDIR) ? \"/\" : \"\";\n+\tif (!null_term_line) {\n+\t\tif (!verbose) {\n+\t\t\twrite_name_quoted(path, stdout, '\\n');\n+\t\t} else {\n+\t\t\tquote_c_style(exclude->src, NULL, stdout, 0);\n+\t\t\tprintf(\":%d:%s%s%s\\t\",\n+\t\t\t       exclude->srcpos,\n+\t\t\t       bang, exclude->pattern, dir);\n+\t\t\tquote_c_style(path, NULL, stdout, 0);\n+\t\t\tfputc('\\n', stdout);\n+\t\t}\n+\t} else {\n+\t\tif (!verbose) {\n+\t\t\tprintf(\"%s%c\", path, '\\0');\n+\t\t} else {\n+\t\t\tprintf(\"%s%c%d%c%s%s%s%c%s%c\",\n+\t\t\t       exclude->src, '\\0',\n+\t\t\t       exclude->srcpos, '\\0',\n+\t\t\t       bang, exclude->pattern, dir, '\\0',\n+\t\t\t       path, '\\0');\n+\t\t}\n+\t}\n+}\n+\n+static int check_ignore(const char *prefix, const char **pathspec)\n+{\n+\tstruct dir_struct dir;\n+\tconst char *path;\n+\tchar *seen = NULL;\n+\tint num_ignored = 0;\n+\n+\t/* read_cache() is only necessary so we can watch out for submodules. */\n+\tif (read_cache() < 0)\n+\t\tdie(_(\"index file corrupt\"));\n+\n+\tmemset(&dir, 0, sizeof(dir));\n+\tdir.flags |= DIR_COLLECT_IGNORED;\n+\tsetup_standard_excludes(&dir);\n+\n+\tif (pathspec) {\n+\t\tint i;\n+\t\tstruct path_exclude_check check;\n+\t\tstruct exclude *exclude;\n+\n+\t\tpath_exclude_check_init(&check, &dir);\n+\t\tif (!seen)\n+\t\t\tseen = find_used_pathspec(pathspec);\n+\t\tfor (i = 0; pathspec[i]; i++) {\n+\t\t\tpath = pathspec[i];\n+\t\t\tchar *full_path =\n+\t\t\t\tprefix_path(prefix, prefix ? strlen(prefix) : 0, path);\n+\t\t\tfull_path = treat_gitlink(full_path);\n+\t\t\tvalidate_path(prefix, full_path);\n+\t\t\tif (!seen[i] && path[0]) {\n+\t\t\t\tint dtype = DT_UNKNOWN;\n+\t\t\t\texclude = last_exclude_matching_path(&check, full_path,\n+\t\t\t\t\t\t\t\t     -1, &dtype);\n+\t\t\t\tif (exclude) {\n+\t\t\t\t\tif (!quiet)\n+\t\t\t\t\t\toutput_exclude(path, exclude);\n+\t\t\t\t\tnum_ignored++;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tfree(seen);\n+\t\tfree_directory(&dir);\n+\t\tpath_exclude_check_clear(&check);\n+\t} else {\n+\t\tprintf(\"no pathspec\\n\");\n+\t}\n+\treturn num_ignored;\n+}\n+\n+static int check_ignore_stdin_paths(const char *prefix)\n+{\n+\tstruct strbuf buf, nbuf;\n+\tchar **pathspec = NULL;\n+\tsize_t nr = 0, alloc = 0;\n+\tint line_termination = null_term_line ? 0 : '\\n';\n+\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_init(&nbuf, 0);\n+\twhile (strbuf_getline(&buf, stdin, line_termination) != EOF) {\n+\t\tif (line_termination && buf.buf[0] == '\"') {\n+\t\t\tstrbuf_reset(&nbuf);\n+\t\t\tif (unquote_c_style(&nbuf, buf.buf, NULL))\n+\t\t\t\tdie(\"line is badly quoted\");\n+\t\t\tstrbuf_swap(&buf, &nbuf);\n+\t\t}\n+\t\tALLOC_GROW(pathspec, nr + 1, alloc);\n+\t\tpathspec[nr] = xcalloc(strlen(buf.buf) + 1, sizeof(*buf.buf));\n+\t\tstrcpy(pathspec[nr++], buf.buf);\n+\t}\n+\tALLOC_GROW(pathspec, nr + 1, alloc);\n+\tpathspec[nr] = NULL;\n+\tint num_ignored = check_ignore(prefix, (const char **)pathspec);\n+\tmaybe_flush_or_die(stdout, \"attribute to stdout\");\n+\tstrbuf_release(&buf);\n+\tstrbuf_release(&nbuf);\n+\tfree(pathspec);\n+\treturn num_ignored;\n+}\n+\n+int cmd_check_ignore(int argc, const char **argv, const char *prefix)\n+{\n+\tgit_config(git_default_config, NULL);\n+\n+\targc = parse_options(argc, argv, prefix, check_ignore_options,\n+\t\t\t     check_ignore_usage, 0);\n+\n+\tif (stdin_paths) {\n+\t\tif (0 < argc)\n+\t\t\tdie(_(\"cannot specify pathnames with --stdin\"));\n+\t} else {\n+\t\tif (null_term_line)\n+\t\t\tdie(_(\"-z only makes sense with --stdin\"));\n+\t\tif (argc == 0)\n+\t\t\tdie(_(\"no path specified\"));\n+\t}\n+\tif (quiet) {\n+\t\tif (argc > 1)\n+\t\t\tdie(_(\"--quiet is only valid with a single pathname\"));\n+\t\tif (verbose)\n+\t\t\tdie(_(\"cannot have both --quiet and --verbose\"));\n+\t}\n+\n+\tint num_ignored = 0;\n+\tif (stdin_paths) {\n+\t\tnum_ignored = check_ignore_stdin_paths(prefix);\n+\t} else {\n+\t\tnum_ignored = check_ignore(prefix, argv);\n+\t\tmaybe_flush_or_die(stdout, \"ignore to stdout\");\n+\t}\n+\n+\treturn num_ignored > 0 ? 0 : 1;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 7e8cfec..bf83303 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -12,6 +12,7 @@ git-branch                              mainporcelain common\n git-bundle                              mainporcelain\n git-cat-file                            plumbinginterrogators\n git-check-attr                          purehelpers\n+git-check-ignore                        purehelpers\n git-checkout                            mainporcelain common\n git-checkout-index                      plumbingmanipulators\n git-check-ref-format                    purehelpers\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex be800e0..1efd366 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -594,6 +594,7 @@ __git_list_porcelain_commands ()\n \t\tarchimport)       : import;;\n \t\tcat-file)         : plumbing;;\n \t\tcheck-attr)       : plumbing;;\n+\t\tcheck-ignore)     : plumbing;;\n \t\tcheck-ref-format) : plumbing;;\n \t\tcheckout-index)   : plumbing;;\n \t\tcommit-tree)      : plumbing;;\ndiff --git a/git.c b/git.c\nindex 8788b32..6caf78a 100644\n--- a/git.c\n+++ b/git.c\n@@ -338,6 +338,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"bundle\", cmd_bundle, RUN_SETUP_GENTLY },\n \t\t{ \"cat-file\", cmd_cat_file, RUN_SETUP },\n \t\t{ \"check-attr\", cmd_check_attr, RUN_SETUP },\n+\t\t{ \"check-ignore\", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"check-ref-format\", cmd_check_ref_format },\n \t\t{ \"checkout\", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"checkout-index\", cmd_checkout_index,\ndiff --git a/t/t0007-ignores.sh b/t/t0007-ignores.sh\nnew file mode 100755\nindex 0000000..a0f571e\n--- /dev/null\n+++ b/t/t0007-ignores.sh\n@@ -0,0 +1,587 @@\n+#!/bin/sh\n+\n+test_description=check-ignore\n+\n+. ./test-lib.sh\n+\n+init_vars () {\n+\tglobal_excludes=\"$HOME/global-excludes\"\n+}\n+\n+enable_global_excludes () {\n+\tinit_vars\n+\tgit config core.excludesfile \"$global_excludes\"\n+}\n+\n+expect_in () {\n+\tdest=\"$HOME/expected-$1\" text=\"$2\"\n+\tif test -z \"$text\"\n+\tthen\n+\t\t>\"$dest\" # avoid newline\n+\telse\n+\t\techo -e \"$text\" >\"$dest\"\n+\tfi\n+}\n+\n+expect () {\n+\texpect_in stdout \"$1\"\n+}\n+\n+expect_from_stdin () {\n+\tcat >\"$HOME/expected-stdout\"\n+}\n+\n+test_stderr () {\n+\texpected=\"$1\"\n+\texpect_in stderr \"$1\" &&\n+\ttest_cmp \"$HOME/expected-stderr\" \"$HOME/stderr\"\n+}\n+\n+stderr_contains () {\n+\tregexp=\"$1\"\n+\tif grep -q \"$regexp\" \"$HOME/stderr\"\n+\tthen\n+\t\treturn 0\n+\telse\n+\t\techo \"didn't find /$regexp/ in $HOME/stderr\"\n+\t\tcat \"$HOME/stderr\"\n+\t\treturn 1\n+\tfi\n+}\n+\n+stderr_empty_on_success () {\n+\texpect_code=\"$1\"\n+\tif test $expect_code = 0\n+\tthen\n+\t\ttest_stderr \"\"\n+\telse\n+\t\t# If we expect failure then stderr might or might not be empty\n+\t\t# due to --quiet - the caller can check its contents\n+\t\treturn 0\n+\tfi\n+}\n+\n+test_check_ignore () {\n+\targs=\"$1\" expect_code=\"${2:-0}\" global_args=\"$3\"\n+\n+\tinit_vars &&\n+\trm -f \"$HOME/stdout\" \"$HOME/stderr\" \"$HOME/cmd\" &&\n+\techo $(which git) $global_args check-ignore $quiet_opt $verbose_opt $args \\\n+\t\t>\"$HOME/cmd\" &&\n+\tpwd >\"$HOME/pwd\" &&\n+\ttest_expect_code \"$expect_code\" \\\n+\t\tgit $global_args check-ignore $quiet_opt $verbose_opt $args \\\n+\t\t>\"$HOME/stdout\" 2>\"$HOME/stderr\" &&\n+\ttest_cmp \"$HOME/expected-stdout\" \"$HOME/stdout\" &&\n+\tstderr_empty_on_success \"$expect_code\"\n+}\n+\n+test_expect_success_multi () {\n+\ttestname=\"$1\" expect_verbose=\"$2\" code=\"$3\"\n+\n+\texpect=$( echo \"$expect_verbose\" | sed -e 's/.*\t//' )\n+\n+\ttest_expect_success \"$testname\" \"\n+\t\texpect '$expect' &&\n+\t\t$code\n+\t\"\n+\n+\tfor quiet_opt in '-q' '--quiet'\n+\tdo\n+\t\ttest_expect_success \"$testname${quiet_opt:+ with $quiet_opt}\" \"\n+\t\t\texpect '' &&\n+\t\t\t$code\n+\t\t\"\n+\tdone\n+\tquiet_opt=\n+\n+\tfor verbose_opt in '-v' '--verbose'\n+\tdo\n+\t\ttest_expect_success \"$testname${verbose_opt:+ with $verbose_opt}\" \"\n+\t\t\texpect '$expect_verbose' &&\n+\t\t\t$code\n+\t\t\"\n+\tdone\n+\tverbose_opt=\n+}\n+\n+test_expect_success 'setup' '\n+\tinit_vars\n+\tmkdir -p a/b/ignored-dir a/submodule b &&\n+\tln -s b a/symlink &&\n+\t(\n+\t\tcd a/submodule &&\n+\t\tgit init &&\n+\t\techo a > a &&\n+\t\tgit add a &&\n+\t\tgit commit -m\"commit in submodule\"\n+\t) &&\n+\tgit add a/submodule &&\n+\tcat <<-\\EOF >.gitignore &&\n+\t\tone\n+\tEOF\n+\tcat <<-\\EOF >a/.gitignore &&\n+\t\ttwo*\n+\t\t*three\n+\tEOF\n+\tcat <<-\\EOF >a/b/.gitignore &&\n+\t\tfour\n+\t\tfive\n+\t\t# this comment should affect the line numbers\n+\t\tsix\n+\t\tignored-dir/\n+\t\t# and so should this blank line:\n+\n+\t\t!on*\n+\t\t!two\n+\tEOF\n+\techo \"seven\" >a/b/ignored-dir/.gitignore &&\n+\ttest -n \"$HOME\" &&\n+\tcat <<-\\EOF >\"$global_excludes\" &&\n+\t\tglobalone\n+\t\t!globaltwo\n+\t\tglobalthree\n+\tEOF\n+\tcat <<-\\EOF >>.git/info/exclude\n+\t\tper-repo\n+\tEOF\n+'\n+\n+############################################################################\n+#\n+# test invalid inputs\n+\n+test_expect_success_multi 'empty command line' '' '\n+\ttest_check_ignore \"\" 128 &&\n+\tstderr_contains \"fatal: no path specified\"\n+'\n+\n+test_expect_success '-q with multiple args' '\n+\texpect \"\" &&\n+\ttest_check_ignore \"-q one two\" 128 &&\n+\tstderr_contains \"fatal: --quiet is only valid with a single pathname\"\n+'\n+\n+test_expect_success '--quiet with multiple args' '\n+\texpect \"\" &&\n+\ttest_check_ignore \"--quiet one two\" 128 &&\n+\tstderr_contains \"fatal: --quiet is only valid with a single pathname\"\n+'\n+\n+for verbose_opt in '-v' '--verbose'\n+do\n+\tfor quiet_opt in '-q' '--quiet'\n+\tdo\n+\t\ttest_expect_success \"$quiet_opt $verbose_opt\" \"\n+\t\t\texpect '' &&\n+\t\t\ttest_check_ignore '$quiet_opt $verbose_opt foo' 128 &&\n+\t\t\tstderr_contains 'fatal: cannot have both --quiet and --verbose'\n+\t\t\"\n+\tdone\n+done\n+\n+test_expect_success '--quiet with multiple args' '\n+\texpect \"\" &&\n+\ttest_check_ignore \"--quiet one two\" 128 &&\n+\tstderr_contains \"fatal: --quiet is only valid with a single pathname\"\n+'\n+\n+test_expect_success_multi 'erroneous use of --' '' '\n+\ttest_check_ignore \"--\" 128 &&\n+\tstderr_contains \"fatal: no path specified\"\n+'\n+\n+test_expect_success_multi '--stdin with superfluous arg' '' '\n+\ttest_check_ignore \"--stdin foo\" 128 &&\n+\tstderr_contains \"fatal: cannot specify pathnames with --stdin\"\n+'\n+\n+test_expect_success_multi '--stdin -z with superfluous arg' '' '\n+\ttest_check_ignore \"--stdin -z foo\" 128 &&\n+\tstderr_contains \"fatal: cannot specify pathnames with --stdin\"\n+'\n+\n+test_expect_success_multi '-z without --stdin' '' '\n+\ttest_check_ignore \"-z\" 128 &&\n+\tstderr_contains \"fatal: -z only makes sense with --stdin\"\n+'\n+\n+test_expect_success_multi '-z without --stdin and superfluous arg' '' '\n+\ttest_check_ignore \"-z foo\" 128 &&\n+\tstderr_contains \"fatal: -z only makes sense with --stdin\"\n+'\n+\n+test_expect_success_multi 'needs work tree' '' '\n+\t(\n+\t\tcd .git &&\n+\t\ttest_check_ignore \"foo\" 128\n+\t) &&\n+\tstderr_contains \"fatal: This operation must be run in a work tree\"\n+'\n+\n+############################################################################\n+#\n+# test standard ignores\n+\n+test_expect_success_multi \"top-level not ignored\" '' '\n+\ttest_check_ignore \"foo\" 1\n+'\n+\n+test_expect_success_multi \"top-level ignored\" \\\n+\t'.gitignore:1:one\tone' '\n+\ttest_check_ignore \"one\"\n+'\n+\n+test_expect_success_multi 'sub-directory ignore from top' \\\n+\t'.gitignore:1:one\ta/one' '\n+\ttest_check_ignore \"a/one\"\n+'\n+\n+test_expect_success 'sub-directory local ignore' '\n+\texpect \"a/3-three\" &&\n+\ttest_check_ignore \"a/3-three a/three-not-this-one\"\n+'\n+\n+test_expect_success 'sub-directory local ignore with --verbose'  '\n+\texpect \"a/.gitignore:2:*three\ta/3-three\" &&\n+\ttest_check_ignore \"--verbose a/3-three a/three-not-this-one\"\n+'\n+\n+test_expect_success 'local ignore inside a sub-directory' '\n+\texpect \"3-three\" &&\n+\t(\n+\t\tcd a &&\n+\t\ttest_check_ignore \"3-three three-not-this-one\"\n+\t)\n+'\n+test_expect_success 'local ignore inside a sub-directory with --verbose' '\n+\texpect \"a/.gitignore:2:*three\t3-three\" &&\n+\t(\n+\t\tcd a &&\n+\t\ttest_check_ignore \"--verbose 3-three three-not-this-one\"\n+\t)\n+'\n+\n+test_expect_success_multi 'nested include' \\\n+\t'a/b/.gitignore:8:!on*\ta/b/one' '\n+\ttest_check_ignore \"a/b/one\"\n+'\n+\n+############################################################################\n+#\n+# test ignored sub-directories\n+\n+test_expect_success_multi 'ignored sub-directory' \\\n+\t'a/b/.gitignore:5:ignored-dir/\ta/b/ignored-dir' '\n+\ttest_check_ignore \"a/b/ignored-dir\"\n+'\n+\n+test_expect_success 'multiple files inside ignored sub-directory' '\n+\texpect_from_stdin <<-\\EOF &&\n+\t\ta/b/ignored-dir/foo\n+\t\ta/b/ignored-dir/twoooo\n+\t\ta/b/ignored-dir/seven\n+\tEOF\n+\ttest_check_ignore \"a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven\"\n+'\n+\n+test_expect_success 'multiple files inside ignored sub-directory with -v' '\n+\texpect_from_stdin <<-\\EOF &&\n+\t\ta/b/.gitignore:5:ignored-dir/\ta/b/ignored-dir/foo\n+\t\ta/b/.gitignore:5:ignored-dir/\ta/b/ignored-dir/twoooo\n+\t\ta/b/.gitignore:5:ignored-dir/\ta/b/ignored-dir/seven\n+\tEOF\n+\ttest_check_ignore \"-v a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven\"\n+'\n+\n+test_expect_success 'cd to ignored sub-directory' '\n+\texpect_from_stdin <<-\\EOF &&\n+\t\tfoo\n+\t\ttwoooo\n+\t\t../one\n+\t\tseven\n+\t\t../../one\n+\tEOF\n+\t(\n+\t\tcd a/b/ignored-dir &&\n+\t\ttest_check_ignore \"foo twoooo ../one seven ../../one\"\n+\t)\n+'\n+\n+test_expect_success 'cd to ignored sub-directory with -v' '\n+\texpect_from_stdin <<-\\EOF &&\n+\t\ta/b/.gitignore:5:ignored-dir/\tfoo\n+\t\ta/b/.gitignore:5:ignored-dir/\ttwoooo\n+\t\ta/b/.gitignore:8:!on*\t../one\n+\t\ta/b/.gitignore:5:ignored-dir/\tseven\n+\t\t.gitignore:1:one\t../../one\n+\tEOF\n+\t(\n+\t\tcd a/b/ignored-dir &&\n+\t\ttest_check_ignore \"-v foo twoooo ../one seven ../../one\"\n+\t)\n+'\n+\n+############################################################################\n+#\n+# test handling of symlinks\n+\n+test_expect_success_multi 'symlink' '' '\n+\ttest_check_ignore \"a/symlink\" 1\n+'\n+\n+test_expect_success_multi 'beyond a symlink' '' '\n+\ttest_check_ignore \"a/symlink/foo\" 128 &&\n+\ttest_stderr \"fatal: '\\''a/symlink/foo'\\'' is beyond a symbolic link\"\n+'\n+\n+test_expect_success_multi 'beyond a symlink from subdirectory' '' '\n+\t(\n+\t\tcd a &&\n+\t\ttest_check_ignore \"symlink/foo\" 128\n+\t) &&\n+\ttest_stderr \"fatal: '\\''symlink/foo'\\'' is beyond a symbolic link\"\n+'\n+\n+############################################################################\n+#\n+# test handling of submodules\n+\n+test_expect_success_multi 'submodule' '' '\n+\ttest_check_ignore \"a/submodule/one\" 128 &&\n+\ttest_stderr \"fatal: Path '\\''a/submodule/one'\\'' is in submodule '\\''a/submodule'\\''\"\n+'\n+\n+test_expect_success_multi 'submodule from subdirectory' '' '\n+\t(\n+\t\tcd a &&\n+\t\ttest_check_ignore \"submodule/one\" 128\n+\t) &&\n+\ttest_stderr \"fatal: Path '\\''a/submodule/one'\\'' is in submodule '\\''a/submodule'\\''\"\n+'\n+\n+############################################################################\n+#\n+# test handling of global ignore files\n+\n+test_expect_success 'global ignore not yet enabled' '\n+\texpect_from_stdin <<-\\EOF &&\n+\t\t.git/info/exclude:7:per-repo\tper-repo\n+\t\ta/.gitignore:2:*three\ta/globalthree\n+\t\t.git/info/exclude:7:per-repo\ta/per-repo\n+\tEOF\n+\ttest_check_ignore \"-v globalone per-repo a/globalthree a/per-repo not-ignored a/globaltwo\"\n+'\n+\n+test_expect_success 'global ignore' '\n+\tenable_global_excludes &&\n+\texpect_from_stdin <<-\\EOF &&\n+\t\tglobalone\n+\t\tper-repo\n+\t\tglobalthree\n+\t\ta/globalthree\n+\t\ta/per-repo\n+\t\tglobaltwo\n+\tEOF\n+\ttest_check_ignore \"globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo\"\n+'\n+\n+test_expect_success 'global ignore with -v' '\n+\tenable_global_excludes &&\n+\texpect_from_stdin <<-EOF &&\n+\t\t$global_excludes:1:globalone\tglobalone\n+\t\t.git/info/exclude:7:per-repo\tper-repo\n+\t\t$global_excludes:3:globalthree\tglobalthree\n+\t\ta/.gitignore:2:*three\ta/globalthree\n+\t\t.git/info/exclude:7:per-repo\ta/per-repo\n+\t\t$global_excludes:2:!globaltwo\tglobaltwo\n+\tEOF\n+\ttest_check_ignore \"-v globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo\"\n+'\n+\n+############################################################################\n+#\n+# test --stdin\n+\n+cat <<-\\EOF >stdin\n+\tone\n+\tnot-ignored\n+\ta/one\n+\ta/not-ignored\n+\ta/b/on\n+\ta/b/one\n+\ta/b/one one\n+\t\"a/b/one two\"\n+\t\"a/b/one\\\"three\"\n+\ta/b/not-ignored\n+\ta/b/two\n+\ta/b/twooo\n+\tglobaltwo\n+\ta/globaltwo\n+\ta/b/globaltwo\n+\tb/globaltwo\n+EOF\n+cat <<-\\EOF >expected-default\n+\tone\n+\ta/one\n+\ta/b/on\n+\ta/b/one\n+\ta/b/one one\n+\ta/b/one two\n+\t\"a/b/one\\\"three\"\n+\ta/b/two\n+\ta/b/twooo\n+\tglobaltwo\n+\ta/globaltwo\n+\ta/b/globaltwo\n+\tb/globaltwo\n+EOF\n+cat <<-EOF >expected-verbose\n+\t.gitignore:1:one\tone\n+\t.gitignore:1:one\ta/one\n+\ta/b/.gitignore:8:!on*\ta/b/on\n+\ta/b/.gitignore:8:!on*\ta/b/one\n+\ta/b/.gitignore:8:!on*\ta/b/one one\n+\ta/b/.gitignore:8:!on*\ta/b/one two\n+\ta/b/.gitignore:8:!on*\t\"a/b/one\\\"three\"\n+\ta/b/.gitignore:9:!two\ta/b/two\n+\ta/.gitignore:1:two*\ta/b/twooo\n+\t$global_excludes:2:!globaltwo\tglobaltwo\n+\t$global_excludes:2:!globaltwo\ta/globaltwo\n+\t$global_excludes:2:!globaltwo\ta/b/globaltwo\n+\t$global_excludes:2:!globaltwo\tb/globaltwo\n+EOF\n+\n+sed -e 's/^\"//' -e 's/\\\\//' -e 's/\"$//' stdin | \\\n+\ttr \"\\n\" \"\\0\" >stdin0\n+sed -e 's/^\"//' -e 's/\\\\//' -e 's/\"$//' expected-default | \\\n+\ttr \"\\n\" \"\\0\" >expected-default0\n+sed -e 's/\t\"/\t/' -e 's/\\\\//' -e 's/\"$//' expected-verbose | \\\n+\ttr \":\\t\\n\" \"\\0\" >expected-verbose0\n+\n+test_expect_success '--stdin' '\n+\texpect_from_stdin <expected-default &&\n+\ttest_check_ignore \"--stdin\" <stdin\n+'\n+\n+test_expect_success '--stdin -q' '\n+\texpect \"\" &&\n+\ttest_check_ignore \"-q --stdin\" <stdin\n+'\n+\n+test_expect_success '--stdin -v' '\n+\texpect_from_stdin <expected-verbose &&\n+\ttest_check_ignore \"-v --stdin\" <stdin\n+'\n+\n+for opts in '--stdin -z' '-z --stdin'\n+do\n+\ttest_expect_success \"$opts\" \"\n+\t\texpect_from_stdin <expected-default0 &&\n+\t\ttest_check_ignore '$opts' <stdin0\n+\t\"\n+\n+\ttest_expect_success \"$opts -q\" \"\n+\t\texpect \"\" &&\n+\t\ttest_check_ignore '-q $opts' <stdin0\n+\t\"\n+\n+\ttest_expect_success \"$opts -v\" \"\n+\t\texpect_from_stdin <expected-verbose0 &&\n+\t\ttest_check_ignore '-v $opts' <stdin0\n+\t\"\n+done\n+\n+cat <<-\\EOF >stdin\n+\t../one\n+\t../not-ignored\n+\tone\n+\tnot-ignored\n+\tb/on\n+\tb/one\n+\tb/one one\n+\t\"b/one two\"\n+\t\"b/one\\\"three\"\n+\tb/two\n+\tb/not-ignored\n+\tb/twooo\n+\t../globaltwo\n+\tglobaltwo\n+\tb/globaltwo\n+\t../b/globaltwo\n+EOF\n+cat <<-\\EOF >expected-default\n+\t../one\n+\tone\n+\tb/on\n+\tb/one\n+\tb/one one\n+\tb/one two\n+\t\"b/one\\\"three\"\n+\tb/two\n+\tb/twooo\n+\t../globaltwo\n+\tglobaltwo\n+\tb/globaltwo\n+\t../b/globaltwo\n+EOF\n+cat <<-EOF >expected-verbose\n+\t.gitignore:1:one\t../one\n+\t.gitignore:1:one\tone\n+\ta/b/.gitignore:8:!on*\tb/on\n+\ta/b/.gitignore:8:!on*\tb/one\n+\ta/b/.gitignore:8:!on*\tb/one one\n+\ta/b/.gitignore:8:!on*\tb/one two\n+\ta/b/.gitignore:8:!on*\t\"b/one\\\"three\"\n+\ta/b/.gitignore:9:!two\tb/two\n+\ta/.gitignore:1:two*\tb/twooo\n+\t$global_excludes:2:!globaltwo\t../globaltwo\n+\t$global_excludes:2:!globaltwo\tglobaltwo\n+\t$global_excludes:2:!globaltwo\tb/globaltwo\n+\t$global_excludes:2:!globaltwo\t../b/globaltwo\n+EOF\n+\n+sed -e 's/^\"//' -e 's/\\\\//' -e 's/\"$//' stdin | \\\n+\ttr \"\\n\" \"\\0\" >stdin0\n+sed -e 's/^\"//' -e 's/\\\\//' -e 's/\"$//' expected-default | \\\n+\ttr \"\\n\" \"\\0\" >expected-default0\n+sed -e 's/\t\"/\t/' -e 's/\\\\//' -e 's/\"$//' expected-verbose | \\\n+\ttr \":\\t\\n\" \"\\0\" >expected-verbose0\n+\n+test_expect_success '--stdin from subdirectory' '\n+\texpect_from_stdin <expected-default &&\n+\t(\n+\t\tcd a &&\n+\t\ttest_check_ignore \"--stdin\" <../stdin\n+\t)\n+'\n+\n+test_expect_success '--stdin from subdirectory with -v' '\n+\texpect_from_stdin <expected-verbose &&\n+\t(\n+\t\tcd a &&\n+\t\ttest_check_ignore \"--stdin -v\" <../stdin\n+\t)\n+'\n+\n+for opts in '--stdin -z' '-z --stdin'\n+do\n+\ttest_expect_success \"$opts from subdirectory\" '\n+\t\texpect_from_stdin <expected-default0 &&\n+\t\t(\n+\t\t\tcd a &&\n+\t\t\ttest_check_ignore \"'\"$opts\"'\" <../stdin0\n+\t\t)\n+\t'\n+\n+\ttest_expect_success \"$opts from subdirectory with -v\" '\n+\t\texpect_from_stdin <expected-verbose0 &&\n+\t\t(\n+\t\t\tcd a &&\n+\t\t\ttest_check_ignore \"'\"$opts\"' -v\" <../stdin0\n+\t\t)\n+\t'\n+done\n+\n+\n+test_done\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 92d7eb4..cce51ac 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -213,19 +213,19 @@ test_expect_success 'general options' '\n '\n \n test_expect_success 'general options plus command' '\n-\ttest_completion \"git --version check\" \"checkout \" &&\n-\ttest_completion \"git --paginate check\" \"checkout \" &&\n-\ttest_completion \"git --git-dir=foo check\" \"checkout \" &&\n-\ttest_completion \"git --bare check\" \"checkout \" &&\n+\ttest_completion \"git --version checko\" \"checkout \" &&\n+\ttest_completion \"git --paginate checko\" \"checkout \" &&\n+\ttest_completion \"git --git-dir=foo checko\" \"checkout \" &&\n+\ttest_completion \"git --bare checko\" \"checkout \" &&\n \ttest_completion \"git --help des\" \"describe \" &&\n-\ttest_completion \"git --exec-path=foo check\" \"checkout \" &&\n-\ttest_completion \"git --html-path check\" \"checkout \" &&\n-\ttest_completion \"git --no-pager check\" \"checkout \" &&\n-\ttest_completion \"git --work-tree=foo check\" \"checkout \" &&\n-\ttest_completion \"git --namespace=foo check\" \"checkout \" &&\n-\ttest_completion \"git --paginate check\" \"checkout \" &&\n-\ttest_completion \"git --info-path check\" \"checkout \" &&\n-\ttest_completion \"git --no-replace-objects check\" \"checkout \"\n+\ttest_completion \"git --exec-path=foo checko\" \"checkout \" &&\n+\ttest_completion \"git --html-path checko\" \"checkout \" &&\n+\ttest_completion \"git --no-pager checko\" \"checkout \" &&\n+\ttest_completion \"git --work-tree=foo checko\" \"checkout \" &&\n+\ttest_completion \"git --namespace=foo checko\" \"checkout \" &&\n+\ttest_completion \"git --paginate checko\" \"checkout \" &&\n+\ttest_completion \"git --info-path checko\" \"checkout \" &&\n+\ttest_completion \"git --no-replace-objects checko\" \"checkout \"\n '\n \n test_done\n-- \n1.7.12.147.g6d168f4\n"},{"id":"199630","messageId":"7v4nms9yja.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"Re: [PATCH v2 00/14] new git check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-20T21:26:01Z","receivedAt":"2012-09-20T21:26:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n> Adam Spiers (14):\n>   Update directory listing API doc to match code\n>   Improve documentation and comments regarding directory traversal API\n>   Rename cryptic 'which' variable to more consistent name\n>   Rename path_excluded() to is_path_excluded()\n>   Rename excluded_from_list() to is_excluded_from_list()\n>   Rename excluded() to is_excluded()\n>   Refactor is_excluded_from_list()\n>   Refactor is_excluded()\n>   Refactor is_path_excluded()\n>   For each exclude pattern, store information about where it came from\n>   Refactor treat_gitlinks()\n>   Extract some useful pathspec handling code from builtin/add.c into a\n>     library\n>   Provide free_directory() for reclaiming dir_struct memory\n>   Add git-check-ignore sub-command\n\nPlease retitle these to have a short \"prefix: \" that names a\nspecific area the series intends to touch.  I retitled your other\nseries to share \"test :\" as their common prefix.\n\nThanks.\n"},{"id":"199632","messageId":"7vzk4k8joy.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"1348170383-15751-11-git-send-email-git@adamspiers.org","subject":"Re: [PATCH v2 10/14] For each exclude pattern, store information about where it came from","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-20T21:31:57Z","receivedAt":"2012-09-20T21:31:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n>  void add_exclude(const char *string, const char *base,\n> -\t\t int baselen, struct exclude_list *el)\n> +\t\t int baselen, struct exclude_list *el, const char *src, int srcpos)\n>  {\n>  \tstruct exclude *x;\n>  \tsize_t len;\n> @@ -341,6 +341,8 @@ void add_exclude(const char *string, const char *base,\n>  \tx->base = base;\n>  \tx->baselen = baselen;\n>  \tx->flags = flags;\n> +\tx->src = src;\n> +\tx->srcpos = srcpos;\n\nHrm, don't all elements \"x\" in \"el\" share the same \"src\", even if\ntheir srcpos may be different?\n\n>  \tif (!strchr(string, '/'))\n>  \t\tx->flags |= EXC_FLAG_NODIR;\n>  \tx->nowildcardlen = simple_length(string);\n> @@ -393,7 +395,7 @@ int add_excludes_from_file_to_list(const char *fname,\n>  \t\t\t\t   int check_index)\n>  {\n>  \tstruct stat st;\n> -\tint fd, i;\n> +\tint fd, i, lineno = 1;\n>  \tsize_t size = 0;\n>  \tchar *buf, *entry;\n>  \n> @@ -438,8 +440,9 @@ int add_excludes_from_file_to_list(const char *fname,\n>  \t\tif (buf[i] == '\\n') {\n>  \t\t\tif (entry != buf + i && entry[0] != '#') {\n>  \t\t\t\tbuf[i - (i && buf[i-1] == '\\r')] = 0;\n> -\t\t\t\tadd_exclude(entry, base, baselen, el);\n> +\t\t\t\tadd_exclude(entry, base, baselen, el, fname, lineno);\n>  \t\t\t}\n> +\t\t\tlineno++;\n>  \t\t\tentry = buf + i + 1;\n>  \t\t}\n>  \t}\n> @@ -474,8 +477,10 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n>  \t\t    !strncmp(dir->basebuf, base, stk->baselen))\n>  \t\t\tbreak;\n>  \t\tdir->exclude_stack = stk->prev;\n> -\t\twhile (stk->exclude_ix < el->nr)\n> -\t\t\tfree(el->excludes[--el->nr]);\n> +\t\twhile (stk->exclude_ix < el->nr) {\n> +\t\t\tstruct exclude *exclude = el->excludes[--el->nr];\n> +\t\t\tfree(exclude);\n> +\t\t}\n>  \t\tfree(stk->filebuf);\n>  \t\tfree(stk);\n>  \t}\n> @@ -502,7 +507,15 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n>  \t\tmemcpy(dir->basebuf + current, base + current,\n>  \t\t       stk->baselen - current);\n>  \t\tstrcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);\n> -\t\tadd_excludes_from_file_to_list(dir->basebuf,\n> +\n> +\t\t/* dir->basebuf gets reused by the traversal, but we\n> +\t\t * need fname to remain unchanged to ensure the src\n> +\t\t * member of each struct exclude correctly back-references\n> +\t\t * its source file.\n> +\t\t */\n> +\t\tchar *fname = strdup(dir->basebuf);\n\n\n\t/*\n         * We try to format our multi-line comments\n         * like this.\n         *\n         * By the way, who owns x->src and who is responsible for\n         * freeing it when the exclude-stack is popped to make them\n         * no longer necessary?\n         *\n         * Oh, by the way, that is a decl-after-statement.\n         */\n\n> +\n> +\t\tadd_excludes_from_file_to_list(fname,\n>  \t\t\t\t\t       dir->basebuf, stk->baselen,\n>  \t\t\t\t\t       &stk->filebuf, el, 1);\n>  \t\tdir->exclude_stack = stk;\n> diff --git a/dir.h b/dir.h\n> index 19beddb..ebb0367 100644\n> --- a/dir.h\n> +++ b/dir.h\n> @@ -31,6 +31,9 @@ struct exclude_list {\n>  \t\tint baselen;\n>  \t\tint to_exclude;\n>  \t\tint flags;\n> +\t\tconst char *src;\n> +\t\tint srcpos; /* counting starts from 1 for line numbers in ignore files,\n> +\t\t\t       and from -1 decrementing for patterns from CLI (--exclude) */\n>  \t} **excludes;\n>  };\n>  \n> @@ -123,7 +126,7 @@ extern int add_excludes_from_file_to_list(const char *fname, const char *base, i\n>  \t\t\t\t\t  char **buf_p, struct exclude_list *el, int check_index);\n>  extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n>  extern void add_exclude(const char *string, const char *base,\n> -\t\t\tint baselen, struct exclude_list *el);\n> +\t\t\tint baselen, struct exclude_list *el, const char *src, int srcpos);\n>  extern void free_excludes(struct exclude_list *el);\n>  extern int file_exists(const char *);\n"},{"id":"199633","messageId":"7vsjac8j52.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"7v4nms9yja.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 00/14] new git check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-20T21:43:53Z","receivedAt":"2012-09-20T21:43:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Adam Spiers <git@adamspiers.org> writes:\n>\n>> Adam Spiers (14):\n>>   Update directory listing API doc to match code\n>>   Improve documentation and comments regarding directory traversal API\n>>   Rename cryptic 'which' variable to more consistent name\n>>   Rename path_excluded() to is_path_excluded()\n>>   Rename excluded_from_list() to is_excluded_from_list()\n>>   Rename excluded() to is_excluded()\n>>   Refactor is_excluded_from_list()\n>>   Refactor is_excluded()\n>>   Refactor is_path_excluded()\n>>   For each exclude pattern, store information about where it came from\n>>   Refactor treat_gitlinks()\n>>   Extract some useful pathspec handling code from builtin/add.c into a\n>>     library\n>>   Provide free_directory() for reclaiming dir_struct memory\n>>   Add git-check-ignore sub-command\n>\n> Please retitle these to have a short \"prefix: \" that names a\n> specific area the series intends to touch.  I retitled your other\n> series to share \"test :\" as their common prefix.\n\nJust to clarify, I think most of them can say \"dir.c: \".\n\nI saw quite a few decl-after-statement in new code.  Please fix\nthem.\n\nAs to the \"who owns x->src and when is it freed\" question, it may\nmake sense to give el a \"filename\" field (command line and other\nspecial cases would get whatever value you deem appropriate, like\nNULL or \"<command line>\"), have x->src point at that field when you\nqueue many x's to the el in add_exc_from_file_to_list().  Then when\nyou pop an element in the exclude_stack, you can free el->filename\nto plug a potential leak.\n\nAlso I do not see why you need to have the strdup() in the caller of\nadd_excludes_from_file_to_list().  If you need to keep it stable\nbecause you are copying it away in exclude or excludde_list,\nwouldn't it make more sense to do that at the beginning of the\ncallee, i.e. add_excludes_from_file_to_list() function?\n"},{"id":"199636","messageId":"CAOkDyE-28Lf8_AO3g8SkiemAhT5OzU=AU+c6rUWB0yPWj-kF2w@mail.gmail.com","threadId":"31414","inReplyTo":"7vsjac8j52.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 00/14] new git check-ignore sub-command","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-09-20T23:45:04Z","receivedAt":"2012-09-20T23:45:04Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Thu, Sep 20, 2012 at 10:43 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>> Adam Spiers <git@adamspiers.org> writes:\n>>> Adam Spiers (14):\n>>>   Update directory listing API doc to match code\n>>>   Improve documentation and comments regarding directory traversal API\n>>>   Rename cryptic 'which' variable to more consistent name\n>>>   Rename path_excluded() to is_path_excluded()\n>>>   Rename excluded_from_list() to is_excluded_from_list()\n>>>   Rename excluded() to is_excluded()\n>>>   Refactor is_excluded_from_list()\n>>>   Refactor is_excluded()\n>>>   Refactor is_path_excluded()\n>>>   For each exclude pattern, store information about where it came from\n>>>   Refactor treat_gitlinks()\n>>>   Extract some useful pathspec handling code from builtin/add.c into a\n>>>     library\n>>>   Provide free_directory() for reclaiming dir_struct memory\n>>>   Add git-check-ignore sub-command\n>>\n>> Please retitle these to have a short \"prefix: \" that names a\n>> specific area the series intends to touch.  I retitled your other\n>> series to share \"test :\" as their common prefix.\n>\n> Just to clarify, I think most of them can say \"dir.c: \".\n\nSure, I can do that, but shouldn't this convention be documented in\nSubmittingPatches?\n\n> I saw quite a few decl-after-statement in new code.  Please fix\n> them.\n\nAgain, I can do that no problem, but again this convention is\nundocumented and I am not psychic ;-)  I see that a patch was provided\n5 years ago to document this, but was apparently never pulled in:\n\n    http://thread.gmane.org/gmane.comp.version-control.git/47843/focus=48015\n\nIt would save everybody's time if these things are documented, since\nthen they wouldn't create noise during the review process.\n\nI also see in the same thread that a patch to add\n-Wdeclaration-after-statement to CFLAGS was also offered but never\npulled in, presumably on the stated grounds that that option was\nrelatively recent 5 years ago.  But wouldn't it be trivial to do\n\n    gcc -v --help 2>&1 | grep declaration-after-statement\n\nat build-time?\n\nI'm also curious to know why this convention exists.  Are people\nreally still compiling git with compilers which don't support C99?\n\n> As to the \"who owns x->src and when is it freed\" question, it may\n> make sense to give el a \"filename\" field (command line and other\n> special cases would get whatever value you deem appropriate, like\n> NULL or \"<command line>\"), have x->src point at that field when you\n> queue many x's to the el in add_exc_from_file_to_list().  Then when\n> you pop an element in the exclude_stack, you can free el->filename\n> to plug a potential leak.\n>\n> Also I do not see why you need to have the strdup() in the caller of\n> add_excludes_from_file_to_list().  If you need to keep it stable\n> because you are copying it away in exclude or excludde_list,\n> wouldn't it make more sense to do that at the beginning of the\n> callee, i.e. add_excludes_from_file_to_list() function?\n\nThanks, I'll take a look at these suggestions tomorrow.\n"},{"id":"199638","messageId":"7vobl0804s.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"CAOkDyE-28Lf8_AO3g8SkiemAhT5OzU=AU+c6rUWB0yPWj-kF2w@mail.gmail.com","subject":"Re: [PATCH v2 00/14] new git check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-21T04:34:27Z","receivedAt":"2012-09-21T04:34:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n> Sure, I can do that, but shouldn't this convention be documented in\n> SubmittingPatches?\n\nPeople must have learned this by imitating what senior contributors\nsend to the list, but the \"[Subject] area: title\" thing does not\nappear in that document.  I agree it should (patches welcome).\n\n>> I saw quite a few decl-after-statement in new code.  Please fix\n>> them.\n>\n> Again, I can do that no problem, but again this convention is\n> undocumented and I am not psychic.\n\nYeah, when there is no code that does decl-after-statement, with the\n\"imitate surrounding code\" rule alone, I agree that it is a bit hard\nto tell we do not allow it (as opposed to seeing a construct is\noften used and assuming that the construct is permitted, which is\nmuch easier).\n\n> I see that a patch was provided\n> 5 years ago to document this, but was apparently never pulled in:\n>\n>     http://thread.gmane.org/gmane.comp.version-control.git/47843/focus=48015\n\nI just read SubmittingPatches again and looked for 1a as found in\nthat patch, and it is there.\n\n> I also see in the same thread that a patch to add\n> -Wdeclaration-after-statement to CFLAGS was also offered but never\n> pulled in,\n\nThere is no guarantee your CC would understand it; you don't even\nknow if it is a gcc in the first place.\n\n> I'm also curious to know why this convention exists.  Are people\n> really still compiling git with compilers which don't support C99?\n\nSee 6d62c98 (Makefile: Change the default compiler from \"gcc\" to\n\"cc\", 2011-12-20).\n"},{"id":"199644","messageId":"505BFED7.5030203@viscovery.net","threadId":"31414","inReplyTo":"1348170383-15751-15-git-send-email-git@adamspiers.org","subject":"Re: [PATCH v2 14/14] Add git-check-ignore sub-command","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2012-09-21T05:44:55Z","receivedAt":"2012-09-21T05:44:55Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 9/20/2012 21:46, schrieb Adam Spiers:\n>  test_expect_success 'general options plus command' '\n> -\ttest_completion \"git --version check\" \"checkout \" &&\n> -\ttest_completion \"git --paginate check\" \"checkout \" &&\n> -\ttest_completion \"git --git-dir=foo check\" \"checkout \" &&\n> -\ttest_completion \"git --bare check\" \"checkout \" &&\n> +\ttest_completion \"git --version checko\" \"checkout \" &&\n> +\ttest_completion \"git --paginate checko\" \"checkout \" &&\n> +\ttest_completion \"git --git-dir=foo checko\" \"checkout \" &&\n> +\ttest_completion \"git --bare checko\" \"checkout \" &&\n> ...\n\nI find this worrysome. Is check-ignore, being a debugging aid, so\nimportant that it must be autocompleted?\n\n-- Hannes\n"},{"id":"199647","messageId":"505C15E4.7060108@alum.mit.edu","threadId":"31414","inReplyTo":"1348170383-15751-15-git-send-email-git@adamspiers.org","subject":"Re: [PATCH v2 14/14] Add git-check-ignore sub-command","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-09-21T07:23:16Z","receivedAt":"2012-09-21T07:23:16Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 09/20/2012 09:46 PM, Adam Spiers wrote:\n> This works in a similar manner to git-check-attr.  Some code\n> was reused from add.c by refactoring out into pathspec.c.\n> \n> Thanks to Jeff King and Junio C Hamano for the idea:\n> http://thread.gmane.org/gmane.comp.version-control.git/108671/focus=108815\n> \n> Signed-off-by: Adam Spiers <git@adamspiers.org>\n> ---\n>  .gitignore                             |   1 +\n>  Documentation/git-check-ignore.txt     |  85 +++++\n>  Documentation/gitignore.txt            |   6 +-\n>  Makefile                               |   1 +\n>  builtin.h                              |   1 +\n>  builtin/check-ignore.c                 | 167 ++++++++++\n>  command-list.txt                       |   1 +\n>  contrib/completion/git-completion.bash |   1 +\n>  git.c                                  |   1 +\n>  t/t0007-ignores.sh                     | 587 +++++++++++++++++++++++++++++++++\n>  t/t9902-completion.sh                  |  24 +-\n>  11 files changed, 861 insertions(+), 14 deletions(-)\n>  create mode 100644 Documentation/git-check-ignore.txt\n>  create mode 100644 builtin/check-ignore.c\n>  create mode 100755 t/t0007-ignores.sh\n> \n> diff --git a/.gitignore b/.gitignore\n> index a188a82..11cd975 100644\n> --- a/.gitignore\n> +++ b/.gitignore\n> @@ -22,6 +22,7 @@\n>  /git-bundle\n>  /git-cat-file\n>  /git-check-attr\n> +/git-check-ignore\n>  /git-check-ref-format\n>  /git-checkout\n>  /git-checkout-index\n> diff --git a/Documentation/git-check-ignore.txt b/Documentation/git-check-ignore.txt\n> new file mode 100644\n> index 0000000..2de4e17\n> --- /dev/null\n> +++ b/Documentation/git-check-ignore.txt\n> @@ -0,0 +1,85 @@\n> +git-check-ignore(1)\n> +=================\n> +\n> +NAME\n> +----\n> +git-check-ignore - Debug gitignore / exclude files\n> +\n> +\n> +SYNOPSIS\n> +--------\n> +[verse]\n> +'git check-ignore' [options] pathname...\n> +'git check-ignore' [options] --stdin < <list-of-paths>\n> +\n> +DESCRIPTION\n> +-----------\n> +\n> +For each pathname given via the command-line or from a file via\n> +`--stdin`, this command will list the first exclude pattern found (if\n> +any) which explicitly excludes or includes that pathname.  Note that\n> +within any given exclude file, later patterns take precedence over\n> +earlier ones, so any matching pattern which this command outputs may\n> +not be the one you would immediately expect.\n\nCan I tell from the output of \"git check-ignore\" whether a file is\nreally ignored?  The way I read the paragraph above, the output doesn't\nnecessarily show the pattern that determines whether a file is *really*\nignored.  That makes it sound like the ignore status of the file might\nbe different than what I would infer from the output.  If I am\nmisunderstanding the situation, then perhaps the explanation in the\nabove paragraph can be improved.\n\nOn the other hand, if my understanding is correct, then why did you\nchoose this (seemingly strange) policy?  It would seem more useful\neither to output the pattern that has the definitive effect on the\nfile's status, or to output all patterns that match the file.\n\n> +OPTIONS\n> +-------\n> +-q, --quiet::\n> +\tDon't output anything, just set exit status.  This is only\n> +\tvalid with a single pathname.\n> +\n> +-v, --verbose::\n> +\tAlso output details about the matching pattern (if any)\n> +\tfor each given pathname.\n> +\n> +--stdin::\n> +\tRead file names from stdin instead of from the command-line.\n> +\n> +-z::\n> +\tThe output format is modified to be machine-parseable (see\n> +\tbelow).  If `--stdin` is also given, input paths are separated\n> +\twith a NUL character instead of a linefeed character.\n> +\n> +OUTPUT\n> +------\n> +\n> +By default, any of the given pathnames which match an ignore pattern\n> +will be output, one per line.  If no pattern matches a given path,\n> +nothing will be output for that path; this means that path will not be\n> +ignored.\n> +\n> +If `--verbose` is specified, the output is a series of lines of the form:\n> +\n> +<source> <COLON> <linenum> <COLON> <pattern> <HT> <pathname>\n> +\n> +<pathname> is the path of a file being queried, <pattern> is the\n> +matching pattern, <source> is the pattern's source file, and <linenum>\n> +is the line number of the pattern within that source.  If the pattern\n> +contained a `!` prefix or `/` suffix, it will be preserved in the\n> +output.  <source> will be an absolute path when referring to the file\n> +configured by `core.excludesfile`, or relative to the repository root\n> +when referring to `.git/info/exclude` or a per-directory exclude file.\n> +\n> +If `-z` is specified, the output is a series of lines of the form:\n> +\n> +EXIT STATUS\n> +-----------\n> [...]\n\nI think you forgot to finish the thought about \"If -z is specified\".\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"199648","messageId":"505C1D27.7080100@alum.mit.edu","threadId":"31414","inReplyTo":"1348170383-15751-13-git-send-email-git@adamspiers.org","subject":"Re: [PATCH v2 12/14] Extract some useful pathspec handling code from builtin/add.c into a library","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-09-21T07:54:15Z","receivedAt":"2012-09-21T07:54:15Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 09/20/2012 09:46 PM, Adam Spiers wrote:\n> This is in preparation for reuse by a new git check-ignore command.\n> \n> Signed-off-by: Adam Spiers <git@adamspiers.org>\n> ---\n>  Makefile      |  2 ++\n>  builtin/add.c | 95 ++-------------------------------------------------------\n>  pathspec.c    | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n>  pathspec.h    |  6 ++++\n>  4 files changed, 108 insertions(+), 92 deletions(-)\n>  create mode 100644 pathspec.c\n>  create mode 100644 pathspec.h\n> \n> [...]\n> diff --git a/pathspec.h b/pathspec.h\n> new file mode 100644\n> index 0000000..4ed40a5\n> --- /dev/null\n> +++ b/pathspec.h\n> @@ -0,0 +1,6 @@\n> +extern void validate_path(const char *prefix, const char *path);\n> +extern const char **validate_pathspec(const char *prefix, const char **files);\n> +extern char *find_used_pathspec(const char **pathspec);\n> +extern void fill_pathspec_matches(const char **pathspec, char *seen, int specs);\n> +extern const char *treat_gitlink(const char **path);\n> +extern void treat_gitlinks(const char **pathspec);\n\nIt was unfortunate that these functions were not already documented.\nBut now that you are elevating them from static to global functions, it\nwould be great if you would add some comments for them.\n\n(By the way, thanks for the docstrings that you added in an earlier patch.)\n\nThis patch is not only moving code around, but also:\n\n* extracting a new function, validate_path()\n\n* changing the signature of validate_pathspec()\n\n* maybe other things?  How is a reviewer to know without examining every\nline of the patch?\n\nEach self-contained change should be done in a separate patch.  For\nexample, one patch should move the code while making only the minimal\nchanges logically connected to the move (e.g., removing \"static\"\nqualifiers).  The other changes should be made separate commits.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"199649","messageId":"505C1F69.1050508@alum.mit.edu","threadId":"31414","inReplyTo":"1348170383-15751-14-git-send-email-git@adamspiers.org","subject":"Re: [PATCH v2 13/14] Provide free_directory() for reclaiming dir_struct memory","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-09-21T08:03:53Z","receivedAt":"2012-09-21T08:03:53Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 09/20/2012 09:46 PM, Adam Spiers wrote:\n> Signed-off-by: Adam Spiers <git@adamspiers.org>\n> ---\n>  Documentation/technical/api-directory-listing.txt |  2 ++\n>  dir.c                                             | 23 +++++++++++++++++++++--\n>  dir.h                                             |  1 +\n>  3 files changed, 24 insertions(+), 2 deletions(-)\n> \n> diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\n> index 944fc39..e339c18 100644\n> --- a/Documentation/technical/api-directory-listing.txt\n> +++ b/Documentation/technical/api-directory-listing.txt\n> @@ -79,4 +79,6 @@ marked. If you to exclude files, make sure you have loaded index first.\n>  \n>  * Use `dir.entries[]`.\n>  \n> +* Call `free_directory()` when none of the contained elements are no longer in use.\n> +\n>  (JC)\n> [...]\n> diff --git a/dir.h b/dir.h\n> index ebb0367..7da29da 100644\n> --- a/dir.h\n> +++ b/dir.h\n> @@ -128,6 +128,7 @@ extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n>  extern void add_exclude(const char *string, const char *base,\n>  \t\t\tint baselen, struct exclude_list *el, const char *src, int srcpos);\n>  extern void free_excludes(struct exclude_list *el);\n> +extern void free_directory(struct dir_struct *dir);\n>  extern int file_exists(const char *);\n>  \n>  extern int is_inside_dir(const char *dir);\n\nWith I see a function like this, the first question in my head is always\n\"does it also free(dir), or does it only free the substructures, leaving\ndir empty but allocated?\"  There should be a comment documenting the\nbehavior.  I also find it helpful if a function that frees the top-level\nstructure has \"free\" in the name, while a function that only empties the\ntop-level structure without freeing it *not* have free in the name\n(e.g., \"clear_directory()\").  But maybe that's just me.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"199663","messageId":"7vfw6b8ifm.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"505C1F69.1050508@alum.mit.edu","subject":"Re: [PATCH v2 13/14] Provide free_directory() for reclaiming dir_struct memory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-21T16:11:25Z","receivedAt":"2012-09-21T16:11:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael Haggerty <mhagger@alum.mit.edu> writes:\n\n> On 09/20/2012 09:46 PM, Adam Spiers wrote:\n>> Signed-off-by: Adam Spiers <git@adamspiers.org>\n>> ---\n>>  Documentation/technical/api-directory-listing.txt |  2 ++\n>>  dir.c                                             | 23 +++++++++++++++++++++--\n>>  dir.h                                             |  1 +\n>>  3 files changed, 24 insertions(+), 2 deletions(-)\n>> \n>> diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\n>> index 944fc39..e339c18 100644\n>> --- a/Documentation/technical/api-directory-listing.txt\n>> +++ b/Documentation/technical/api-directory-listing.txt\n>> @@ -79,4 +79,6 @@ marked. If you to exclude files, make sure you have loaded index first.\n>>  \n>>  * Use `dir.entries[]`.\n>>  \n>> +* Call `free_directory()` when none of the contained elements are no longer in use.\n>> +\n>>  (JC)\n>> [...]\n>> diff --git a/dir.h b/dir.h\n>> index ebb0367..7da29da 100644\n>> --- a/dir.h\n>> +++ b/dir.h\n>> @@ -128,6 +128,7 @@ extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n>>  extern void add_exclude(const char *string, const char *base,\n>>  \t\t\tint baselen, struct exclude_list *el, const char *src, int srcpos);\n>>  extern void free_excludes(struct exclude_list *el);\n>> +extern void free_directory(struct dir_struct *dir);\n>>  extern int file_exists(const char *);\n>>  \n>>  extern int is_inside_dir(const char *dir);\n>\n> With I see a function like this, the first question in my head is always\n> \"does it also free(dir), or does it only free the substructures, leaving\n> dir empty but allocated?\"  There should be a comment documenting the\n> behavior.  I also find it helpful if a function that frees the top-level\n> structure has \"free\" in the name, while a function that only empties the\n> top-level structure without freeing it *not* have free in the name\n> (e.g., \"clear_directory()\").  But maybe that's just me.\n\nAll good points, including the last one.  Thanks.\n"},{"id":"199664","messageId":"7vbogz8ho2.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"505C15E4.7060108@alum.mit.edu","subject":"Re: [PATCH v2 14/14] Add git-check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-21T16:27:57Z","receivedAt":"2012-09-21T16:27:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael Haggerty <mhagger@alum.mit.edu> writes:\n\n>> +For each pathname given via the command-line or from a file via\n>> +`--stdin`, this command will list the first exclude pattern found (if\n>> +any) which explicitly excludes or includes that pathname.  Note that\n>> +within any given exclude file, later patterns take precedence over\n>> +earlier ones, so any matching pattern which this command outputs may\n>> +not be the one you would immediately expect.\n>\n> Can I tell from the output of \"git check-ignore\" whether a file is\n> really ignored?  The way I read the paragraph above, the output doesn't\n> necessarily show the pattern that determines whether a file is *really*\n> ignored.  That makes it sound like the ignore status of the file might\n> be different than what I would infer from the output.  If I am\n> misunderstanding the situation, then perhaps the explanation in the\n> above paragraph can be improved.\n>\n> On the other hand, if my understanding is correct, then why did you\n> choose this (seemingly strange) policy?  It would seem more useful\n> either to output the pattern that has the definitive effect on the\n> file's status, or to output all patterns that match the file.\n\nVery good point. I didn't look at this patch at all, but I would\nguess the patch hooked this into the order in which the existing\nignore mechanism computes the match, and \"the first I find\" Adam\nsays is written from implementation point of view.\n\nAnd that is a wrong way to go about it.\n\nThe existing document for the ignore mechanism explains things from\nthe end user perspective, i.e. within a file, the last matching\npattern determines the fate of the path.  And this debugging aid\nshould report which pattern from what source determined the fate of\nthe path, so from the end user perspective, saying \"first\" is\nnonsense.\n\nIt just happens that the implementation optimizes by scanning the\nlist of patterns \"backwards\" and stops at the \"first\" hit, which is\nthe last matching pattern that determines the fate of the path.\n\nThe documentation of this debugging aid should use the same phrasing\n\"git help ignore\" uses and say \"show the pattern that decides the\noutcome\", and lose that confusing \"Note that\" that is not helping\nanybody.\n"},{"id":"199684","messageId":"7vehlv5hg8.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"Re: [PATCH v2 00/14] new git check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-21T19:00:55Z","receivedAt":"2012-09-21T19:00:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n> It has been rebased on the latest master, and passed a full test run.\n\nFYI, I applied the attached on top before queuing it in 'pu'.\n\nPoints to note:\n\n * We match the underline and the title of documentation header;\n\n * a few type mismatches (constness of full_path and treat_gitlink()\n   signature) that broke compilation;\n\n * decl-after-stmt;\n\n * multi-line comment style;\n\nThanks.\n\n-- >8 --\nSubject: check-ignore: minimum compilation fix\n\n---\n Documentation/git-check-ignore.txt |  2 +-\n builtin/check-ignore.c             | 11 +++++++----\n dir.c                              | 12 ++++++------\n pathspec.c                         |  4 ++--\n pathspec.h                         |  2 +-\n 5 files changed, 17 insertions(+), 14 deletions(-)\n\ndiff --git a/Documentation/git-check-ignore.txt b/Documentation/git-check-ignore.txt\nindex 2de4e17..96fa7bc 100644\n--- a/Documentation/git-check-ignore.txt\n+++ b/Documentation/git-check-ignore.txt\n@@ -1,5 +1,5 @@\n git-check-ignore(1)\n-=================\n+===================\n \n NAME\n ----\ndiff --git a/builtin/check-ignore.c b/builtin/check-ignore.c\nindex 74ea2fc..3cbd3b9 100644\n--- a/builtin/check-ignore.c\n+++ b/builtin/check-ignore.c\n@@ -77,9 +77,10 @@ static int check_ignore(const char *prefix, const char **pathspec)\n \t\tif (!seen)\n \t\t\tseen = find_used_pathspec(pathspec);\n \t\tfor (i = 0; pathspec[i]; i++) {\n+\t\t\tconst char *full_path;\n \t\t\tpath = pathspec[i];\n-\t\t\tchar *full_path =\n-\t\t\t\tprefix_path(prefix, prefix ? strlen(prefix) : 0, path);\n+\t\t\tfull_path = prefix_path(prefix, prefix\n+\t\t\t\t\t\t? strlen(prefix) : 0, path);\n \t\t\tfull_path = treat_gitlink(full_path);\n \t\t\tvalidate_path(prefix, full_path);\n \t\t\tif (!seen[i] && path[0]) {\n@@ -108,6 +109,7 @@ static int check_ignore_stdin_paths(const char *prefix)\n \tchar **pathspec = NULL;\n \tsize_t nr = 0, alloc = 0;\n \tint line_termination = null_term_line ? 0 : '\\n';\n+\tint num_ignored;\n \n \tstrbuf_init(&buf, 0);\n \tstrbuf_init(&nbuf, 0);\n@@ -124,7 +126,7 @@ static int check_ignore_stdin_paths(const char *prefix)\n \t}\n \tALLOC_GROW(pathspec, nr + 1, alloc);\n \tpathspec[nr] = NULL;\n-\tint num_ignored = check_ignore(prefix, (const char **)pathspec);\n+\tnum_ignored = check_ignore(prefix, (const char **)pathspec);\n \tmaybe_flush_or_die(stdout, \"attribute to stdout\");\n \tstrbuf_release(&buf);\n \tstrbuf_release(&nbuf);\n@@ -134,6 +136,8 @@ static int check_ignore_stdin_paths(const char *prefix)\n \n int cmd_check_ignore(int argc, const char **argv, const char *prefix)\n {\n+\tint num_ignored = 0;\n+\n \tgit_config(git_default_config, NULL);\n \n \targc = parse_options(argc, argv, prefix, check_ignore_options,\n@@ -155,7 +159,6 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix)\n \t\t\tdie(_(\"cannot have both --quiet and --verbose\"));\n \t}\n \n-\tint num_ignored = 0;\n \tif (stdin_paths) {\n \t\tnum_ignored = check_ignore_stdin_paths(prefix);\n \t} else {\ndiff --git a/dir.c b/dir.c\nindex d516ddf..8fc162c 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -511,14 +511,13 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t\t       stk->baselen - current);\n \t\tstrcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);\n \n-\t\t/* dir->basebuf gets reused by the traversal, but we\n+\t\t/*\n+\t\t * dir->basebuf gets reused by the traversal, but we\n \t\t * need fname to remain unchanged to ensure the src\n \t\t * member of each struct exclude correctly back-references\n \t\t * its source file.\n \t\t */\n-\t\tchar *fname = strdup(dir->basebuf);\n-\n-\t\tadd_excludes_from_file_to_list(fname,\n+\t\tadd_excludes_from_file_to_list(strdup(dir->basebuf),\n \t\t\t\t\t       dir->basebuf, stk->baselen,\n \t\t\t\t\t       &stk->filebuf, el, 1);\n \t\tdir->exclude_stack = stk;\n@@ -1479,13 +1478,14 @@ void free_pathspec(struct pathspec *pathspec)\n \n void free_directory(struct dir_struct *dir)\n {\n+\tstruct exclude_stack *stk;\n \tint st;\n \tfor (st = EXC_CMDL; st <= EXC_FILE; st++)\n \t\tfree_excludes(&dir->exclude_list[st]);\n \n-\tstruct exclude_stack *prev, *stk = dir->exclude_stack;\n+\tstk = dir->exclude_stack;\n \twhile (stk) {\n-\t\tprev = stk->prev;\n+\t\tstruct exclude_stack *prev = stk->prev;\n \t\tfree_exclude_stack(stk);\n \t\tstk = prev;\n \t}\ndiff --git a/pathspec.c b/pathspec.c\nindex 10f6643..9525c7c 100644\n--- a/pathspec.c\n+++ b/pathspec.c\n@@ -88,10 +88,10 @@ const char *treat_gitlink(const char *path)\n \n void treat_gitlinks(const char **pathspec)\n {\n+\tint i;\n+\n \tif (!pathspec || !*pathspec)\n \t\treturn;\n-\n-\tint i;\n \tfor (i = 0; pathspec[i]; i++)\n \t\tpathspec[i] = treat_gitlink(pathspec[i]);\n }\ndiff --git a/pathspec.h b/pathspec.h\nindex 4ed40a5..b7c053a 100644\n--- a/pathspec.h\n+++ b/pathspec.h\n@@ -2,5 +2,5 @@ extern void validate_path(const char *prefix, const char *path);\n extern const char **validate_pathspec(const char *prefix, const char **files);\n extern char *find_used_pathspec(const char **pathspec);\n extern void fill_pathspec_matches(const char **pathspec, char *seen, int specs);\n-extern const char *treat_gitlink(const char **path);\n+extern const char *treat_gitlink(const char *path);\n extern void treat_gitlinks(const char **pathspec);\n-- \n1.7.12.1.441.gae568b5\n"},{"id":"199688","messageId":"7v392b5fjr.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"1348170383-15751-15-git-send-email-git@adamspiers.org","subject":"Re: [PATCH v2 14/14] Add git-check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-21T19:42:00Z","receivedAt":"2012-09-21T19:42:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n> +expect_in () {\n> +\tdest=\"$HOME/expected-$1\" text=\"$2\"\n> +\tif test -z \"$text\"\n> +\tthen\n> +\t\t>\"$dest\" # avoid newline\n> +\telse\n> +\t\techo -e \"$text\" >\"$dest\"\n\nThis breaks when your shell is not \"bash\".\n\n> +test_check_ignore () {\n> +\targs=\"$1\" expect_code=\"${2:-0}\" global_args=\"$3\"\n> +\n> +\tinit_vars &&\n> +\trm -f \"$HOME/stdout\" \"$HOME/stderr\" \"$HOME/cmd\" &&\n> +\techo $(which git) $global_args check-ignore $quiet_opt $verbose_opt $args \\\n> +\t\t>\"$HOME/cmd\" &&\n\nDon't use which in scripts.  Is this just leftover cruft from debugging?\n"},{"id":"199863","messageId":"7v7grjxdbu.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"1348170383-15751-1-git-send-email-git@adamspiers.org","subject":"Re: [PATCH v2 00/14] new git check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-24T22:31:33Z","receivedAt":"2012-09-24T22:31:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Adam Spiers <git@adamspiers.org> writes:\n\n>  t/t0007-ignores.sh                                | 587 ++++++++++++++++++++++\n\nI needed this to make it pass the test. I didn't see anything that\nis passed to expect_in via $text that would benefit from \"echo -e\"\nbut I did not look too carefully; you may have had a reason to write\n\"-e\" there, and this patch may break for somebody else (namely, you),\nin which case please explain and justify the use of \"-e\" better. We\nneed a solution that does not rely on the bash-ism.\n\nThanks.\n\n-- >8 --\n\n\"echo -e\" is a bash-ism that does not seem to be needed in the\ncontext of this test (both bash and dash passes the test without\nthe extraneous \"-e\", but having \"-e\" breaks test run under dash).\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t0007-ignores.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t0007-ignores.sh b/t/t0007-ignores.sh\nindex a0f571e..7fd7e53 100755\n--- a/t/t0007-ignores.sh\n+++ b/t/t0007-ignores.sh\n@@ -19,7 +19,7 @@ expect_in () {\n \tthen\n \t\t>\"$dest\" # avoid newline\n \telse\n-\t\techo -e \"$text\" >\"$dest\"\n+\t\techo \"$text\" >\"$dest\"\n \tfi\n }\n \n-- \n1.7.12.1.451.gb433296\n"},{"id":"199915","messageId":"7vk3vhvg5b.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"505BFED7.5030203@viscovery.net","subject":"Re: [PATCH v2 14/14] Add git-check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-25T23:25:52Z","receivedAt":"2012-09-25T23:25:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j.sixt@viscovery.net> writes:\n\n> Am 9/20/2012 21:46, schrieb Adam Spiers:\n>>  test_expect_success 'general options plus command' '\n>> -\ttest_completion \"git --version check\" \"checkout \" &&\n>> -\ttest_completion \"git --paginate check\" \"checkout \" &&\n>> -\ttest_completion \"git --git-dir=foo check\" \"checkout \" &&\n>> -\ttest_completion \"git --bare check\" \"checkout \" &&\n>> +\ttest_completion \"git --version checko\" \"checkout \" &&\n>> +\ttest_completion \"git --paginate checko\" \"checkout \" &&\n>> +\ttest_completion \"git --git-dir=foo checko\" \"checkout \" &&\n>> +\ttest_completion \"git --bare checko\" \"checkout \" &&\n>> ...\n>\n> I find this worrysome. Is check-ignore, being a debugging aid, so\n> important that it must be autocompleted?\n\nThe shell function __git_list_porcelain_commands in contrib/completion/\nstarts from \"git help -a\" and filters plumbing commands and helpers\nvia a blacklist.  At least, check-ignore needs to be added there.\n\nThese days, we do not add random subcommands willy-nilly (I still\ndoubt if check-ignore needs to be a separate debugging command, or a\nnew mode of operation of ls-files or something), so the approach to\nuse a blacklist makes more sense.  \"help -a\" is designed to show\nwhatever the users throw in their ~/bin (assuming that is on $PATH)\nunder git-whatever name, and we _do_ want to complete \"git wh<TAB>\"\nto that custom command, so a whitelist-based solution is unwieldy to\nconstruct.\n"},{"id":"199926","messageId":"50629765.2060107@viscovery.net","threadId":"31414","inReplyTo":"7vk3vhvg5b.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 14/14] Add git-check-ignore sub-command","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2012-09-26T05:49:25Z","receivedAt":"2012-09-26T05:49:25Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 9/26/2012 1:25, schrieb Junio C Hamano:\n> Johannes Sixt <j.sixt@viscovery.net> writes:\n> \n>> Am 9/20/2012 21:46, schrieb Adam Spiers:\n>>>  test_expect_success 'general options plus command' '\n>>> -\ttest_completion \"git --version check\" \"checkout \" &&\n>>> -\ttest_completion \"git --paginate check\" \"checkout \" &&\n>>> -\ttest_completion \"git --git-dir=foo check\" \"checkout \" &&\n>>> -\ttest_completion \"git --bare check\" \"checkout \" &&\n>>> +\ttest_completion \"git --version checko\" \"checkout \" &&\n>>> +\ttest_completion \"git --paginate checko\" \"checkout \" &&\n>>> +\ttest_completion \"git --git-dir=foo checko\" \"checkout \" &&\n>>> +\ttest_completion \"git --bare checko\" \"checkout \" &&\n>>> ...\n>>\n>> I find this worrysome. Is check-ignore, being a debugging aid, so\n>> important that it must be autocompleted?\n> \n> The shell function __git_list_porcelain_commands in contrib/completion/\n> starts from \"git help -a\" and filters plumbing commands and helpers\n> via a blacklist.  At least, check-ignore needs to be added there.\n> \n> These days, we do not add random subcommands willy-nilly (I still\n> doubt if check-ignore needs to be a separate debugging command, or a\n> new mode of operation of ls-files or something), so the approach to\n> use a blacklist makes more sense.  \"help -a\" is designed to show\n> whatever the users throw in their ~/bin (assuming that is on $PATH)\n> under git-whatever name, and we _do_ want to complete \"git wh<TAB>\"\n> to that custom command, so a whitelist-based solution is unwieldy to\n> construct.\n\nWe already have 'git check-attr', but it is obviously not among the\nautocompleted commands, otherwise the above test would not have passed.\nIMO, 'git check-ignore' falls into the same category as 'git check-attr'\nwith regard to completion.\n\n-- Hannes\n"},{"id":"199945","messageId":"7vy5jwubig.fsf@alter.siamese.dyndns.org","threadId":"31414","inReplyTo":"50629765.2060107@viscovery.net","subject":"Re: [PATCH v2 14/14] Add git-check-ignore sub-command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-26T14:03:35Z","receivedAt":"2012-09-26T14:03:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j.sixt@viscovery.net> writes:\n\n>> These days, we do not add random subcommands willy-nilly (I still\n>> doubt if check-ignore needs to be a separate debugging command, or a\n>> new mode of operation of ls-files or something), so the approach to\n>> use a blacklist makes more sense.  \"help -a\" is designed to show\n>> whatever the users throw in their ~/bin (assuming that is on $PATH)\n>> under git-whatever name, and we _do_ want to complete \"git wh<TAB>\"\n>> to that custom command, so a whitelist-based solution is unwieldy to\n>> construct.\n>\n> We already have 'git check-attr', but it is obviously not among the\n> autocompleted commands, otherwise the above test would not have passed.\n> IMO, 'git check-ignore' falls into the same category as 'git check-attr'\n> with regard to completion.\n\nExactly.\n\nActually I think what happened was that the submitter didn't have\nchange to contrib/completion/ in earlier private versions, saw the\ntest fail and updated t9902 without thinking.  The patch posted has\naddition to contrib/completion/ that blacklists check-ignore the\nsame way as check-attr, which made the change to t9902 unnecessary\nbut because the update was done without thinking, it wasn't even\nrealized that the test would have passed without the patch to it.\n\nReverting the part that touches t9902 should still pass, I would\nthink.\n\n t/t9902-completion.sh | 24 ++++++++++++------------\n 1 file changed, 12 insertions(+), 12 deletions(-)\n\ndiff --git c/t/t9902-completion.sh w/t/t9902-completion.sh\nindex cce51ac..92d7eb4 100755\n--- c/t/t9902-completion.sh\n+++ w/t/t9902-completion.sh\n@@ -213,19 +213,19 @@ test_expect_success 'general options' '\n '\n \n test_expect_success 'general options plus command' '\n-\ttest_completion \"git --version checko\" \"checkout \" &&\n-\ttest_completion \"git --paginate checko\" \"checkout \" &&\n-\ttest_completion \"git --git-dir=foo checko\" \"checkout \" &&\n-\ttest_completion \"git --bare checko\" \"checkout \" &&\n+\ttest_completion \"git --version check\" \"checkout \" &&\n+\ttest_completion \"git --paginate check\" \"checkout \" &&\n+\ttest_completion \"git --git-dir=foo check\" \"checkout \" &&\n+\ttest_completion \"git --bare check\" \"checkout \" &&\n \ttest_completion \"git --help des\" \"describe \" &&\n-\ttest_completion \"git --exec-path=foo checko\" \"checkout \" &&\n-\ttest_completion \"git --html-path checko\" \"checkout \" &&\n-\ttest_completion \"git --no-pager checko\" \"checkout \" &&\n-\ttest_completion \"git --work-tree=foo checko\" \"checkout \" &&\n-\ttest_completion \"git --namespace=foo checko\" \"checkout \" &&\n-\ttest_completion \"git --paginate checko\" \"checkout \" &&\n-\ttest_completion \"git --info-path checko\" \"checkout \" &&\n-\ttest_completion \"git --no-replace-objects checko\" \"checkout \"\n+\ttest_completion \"git --exec-path=foo check\" \"checkout \" &&\n+\ttest_completion \"git --html-path check\" \"checkout \" &&\n+\ttest_completion \"git --no-pager check\" \"checkout \" &&\n+\ttest_completion \"git --work-tree=foo check\" \"checkout \" &&\n+\ttest_completion \"git --namespace=foo check\" \"checkout \" &&\n+\ttest_completion \"git --paginate check\" \"checkout \" &&\n+\ttest_completion \"git --info-path check\" \"checkout \" &&\n+\ttest_completion \"git --no-replace-objects check\" \"checkout \"\n '\n \n test_done\n"},{"id":"204991","messageId":"20121216230411.GA3673@gmail.com","threadId":"31414","inReplyTo":"7vehlv5hg8.fsf@alter.siamese.dyndns.org","subject":"compiler checks","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-12-16T23:04:11Z","receivedAt":"2012-12-16T23:04:11Z","isPatch":false,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Fri, Sep 21, 2012 at 12:00:55PM -0700, Junio C Hamano wrote:\n> Adam Spiers <git@adamspiers.org> writes:\n> \n> > It has been rebased on the latest master, and passed a full test run.\n> \n> FYI, I applied the attached on top before queuing it in 'pu'.\n> \n> Points to note:\n> \n>  * We match the underline and the title of documentation header;\n> \n>  * a few type mismatches (constness of full_path and treat_gitlink()\n>    signature) that broke compilation;\n\nOf course I will incorporate these tweaks in my re-roll, but it\nworries me that my environment yielded no compilation issues even\nwithout these tweaks.  Obviously I wouldn't have dreamed of submitting\na patch series which didn't even compile!  I'm using a modern gcc, and\nI guess you probably are too?  Which would suggest to me that either\nyour environment is somehow set up to perform stricter type checking\nthan mine[1], or that there's a weird compiler-oriented bug somewhere\n(e.g. in Makefile).  Or maybe I'm missing something obvious ...\n\n[1] I'm in favour of stricter compiler checks where possible:\n    http://article.gmane.org/gmane.comp.version-control.git/211607\n"},{"id":"205495","messageId":"CAOkDyE_hs9kVtk4DbN+0gTaud35oLv4S4wiuAgYmtxCnPuUzhA@mail.gmail.com","threadId":"31414","inReplyTo":"7vsjac8j52.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 00/14] new git check-ignore sub-command","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-12-26T15:44:32Z","receivedAt":"2012-12-26T15:44:32Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Thu, Sep 20, 2012 at 10:43 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>> Adam Spiers <git@adamspiers.org> writes:\n>>> Adam Spiers (14):\n>>>   Update directory listing API doc to match code\n>>>   Improve documentation and comments regarding directory traversal API\n>>>   Rename cryptic 'which' variable to more consistent name\n>>>   Rename path_excluded() to is_path_excluded()\n>>>   Rename excluded_from_list() to is_excluded_from_list()\n>>>   Rename excluded() to is_excluded()\n>>>   Refactor is_excluded_from_list()\n>>>   Refactor is_excluded()\n>>>   Refactor is_path_excluded()\n>>>   For each exclude pattern, store information about where it came from\n>>>   Refactor treat_gitlinks()\n>>>   Extract some useful pathspec handling code from builtin/add.c into a\n>>>     library\n>>>   Provide free_directory() for reclaiming dir_struct memory\n>>>   Add git-check-ignore sub-command\n\n[snipped]\n\n> As to the \"who owns x->src and when is it freed\" question, it may\n> make sense to give el a \"filename\" field (command line and other\n> special cases would get whatever value you deem appropriate, like\n> NULL or \"<command line>\"), have x->src point at that field when you\n> queue many x's to the el in add_exc_from_file_to_list().  Then when\n> you pop an element in the exclude_stack, you can free el->filename\n> to plug a potential leak.\n\nI have done this, but it required a change to struct dir:\n\nCurrently, when dir->exclude_stack is more than one entry deep, the\nexclude_list pointed to by dir->exclude_list[EXC_DIRS] is a\nconcatenation of exclude elements from multiple files, so there will\nbe different values for src.  The same is true of EXC_FILE, which\ntypically mixes patterns from .git/info/exclude and core.excludesfile.\nTherefore I have split each exclude_list into potentially multiple\nexclude_lists, one per pattern source, whilst preserving the EXC_*\ngrouping and ordering.\n\nMy latest re-roll of as/check-ignore is nearly ready, and when I send\nit, you will see a new patch in there covering the above change.\n\n> Also I do not see why you need to have the strdup() in the caller of\n> add_excludes_from_file_to_list().  If you need to keep it stable\n> because you are copying it away in exclude or excludde_list,\n> wouldn't it make more sense to do that at the beginning of the\n> callee, i.e. add_excludes_from_file_to_list() function?\n\nNo, because in all other callers of add_excludes_from_file_to_list(),\nthe exclude source is already stable.  The re-roll will make this\nclearer.\n"},{"id":"205496","messageId":"CAOkDyE-HEAiBPyLLTRs2gY1C9uBSmX+xG6NqzLeoWh-oYb0LUA@mail.gmail.com","threadId":"31414","inReplyTo":"7vzk4k8joy.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 10/14] For each exclude pattern, store information about where it came from","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2012-12-26T15:46:17Z","receivedAt":"2012-12-26T15:46:17Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"On Thu, Sep 20, 2012 at 02:31:57PM -0700, Junio C Hamano wrote:\n> Adam Spiers <git@adamspiers.org> writes:\n>\n> >  void add_exclude(const char *string, const char *base,\n> > -\t\t int baselen, struct exclude_list *el)\n> > +\t\t int baselen, struct exclude_list *el, const char *src, int srcpos)\n> >  {\n> >  \tstruct exclude *x;\n> >  \tsize_t len;\n> > @@ -341,6 +341,8 @@ void add_exclude(const char *string, const char *base,\n> >  \tx->base = base;\n> >  \tx->baselen = baselen;\n> >  \tx->flags = flags;\n> > +\tx->src = src;\n> > +\tx->srcpos = srcpos;\n>\n> Hrm, don't all elements \"x\" in \"el\" share the same \"src\", even if\n> their srcpos may be different?\n\nNo not currently - please see the other mail I just sent to the\n[PATCH v2 00/14] thread.\n"}]}