{"thread":{"id":"10914","subject":"[PATCH] file_exists(): dangling symlinks do exist","startedAt":"2007-11-18T10:21:24Z","lastAt":"2007-11-19T16:47:35Z","messageCount":8,"participants":["Junio C Hamano","Kristian Høgsberg"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"60220","messageId":"1195381287-26823-1-git-send-email-gitster@pobox.com","threadId":"10914","inReplyTo":null,"subject":"[PATCH] file_exists(): dangling symlinks do exist","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-18T10:21:24Z","receivedAt":"2007-11-18T10:21:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This function is used to see if a path given by the user does exist\non the filesystem.  A symbolic link that does not point anywhere does\nexist but running stat() on it would yield an error, and it incorrectly\nsaid it does not exist.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n dir.c |    7 +++----\n 1 files changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 01790ab..87e5dec 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -688,11 +688,10 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i\n \treturn dir->nr;\n }\n \n-int\n-file_exists(const char *f)\n+int file_exists(const char *f)\n {\n-  struct stat sb;\n-  return stat(f, &sb) == 0;\n+\tstruct stat sb;\n+\treturn lstat(f, &sb) == 0;\n }\n \n /*\n-- \n1.5.3.5.1815.g9445b\n"},{"id":"60223","messageId":"1195381287-26823-2-git-send-email-gitster@pobox.com","threadId":"10914","inReplyTo":"1195381287-26823-1-git-send-email-gitster@pobox.com","subject":"[PATCH] Export three helper functions from ls-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-18T10:21:25Z","receivedAt":"2007-11-18T10:21:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This exports three helper functions from ls-files.\n\n * pathspec_match() checks if a given path matches a set of pathspecs\n   and optionally records which pathspec was used.  This function used\n   to be called \"match()\" but renamed to be a bit less vague.\n\n * report_path_error() takes a set of pathspecs and the record\n   pathspec_match() above leaves, and gives error message.  This\n   was split out of the main function of ls-files.\n\n * overlay_tree_on_cache() takes a tree-ish (typically \"HEAD\")\n   and overlays it on the current in-core index.  By iterating\n   over the resulting index, the caller can find out the paths\n   in either the index or the HEAD.  This function used to be\n   called \"overlay_tree()\" but renamed to be a bit more\n   descriptive.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-ls-files.c |   98 +++++++++++++++++++++++++++------------------------\n cache.h            |    6 +++\n 2 files changed, 58 insertions(+), 46 deletions(-)\n\ndiff --git a/builtin-ls-files.c b/builtin-ls-files.c\nindex e0b856f..be485bb 100644\n--- a/builtin-ls-files.c\n+++ b/builtin-ls-files.c\n@@ -38,28 +38,28 @@ static const char *tag_modified = \"\";\n \n \n /*\n- * Match a pathspec against a filename. The first \"len\" characters\n+ * Match a pathspec against a filename. The first \"skiplen\" characters\n  * are the common prefix\n  */\n-static int match(const char **spec, char *ps_matched,\n-\t\t const char *filename, int len)\n+int pathspec_match(const char **spec, char *ps_matched,\n+\t\t   const char *filename, int skiplen)\n {\n \tconst char *m;\n \n \twhile ((m = *spec++) != NULL) {\n-\t\tint matchlen = strlen(m + len);\n+\t\tint matchlen = strlen(m + skiplen);\n \n \t\tif (!matchlen)\n \t\t\tgoto matched;\n-\t\tif (!strncmp(m + len, filename + len, matchlen)) {\n-\t\t\tif (m[len + matchlen - 1] == '/')\n+\t\tif (!strncmp(m + skiplen, filename + skiplen, matchlen)) {\n+\t\t\tif (m[skiplen + matchlen - 1] == '/')\n \t\t\t\tgoto matched;\n-\t\t\tswitch (filename[len + matchlen]) {\n+\t\t\tswitch (filename[skiplen + matchlen]) {\n \t\t\tcase '/': case '\\0':\n \t\t\t\tgoto matched;\n \t\t\t}\n \t\t}\n-\t\tif (!fnmatch(m + len, filename + len, 0))\n+\t\tif (!fnmatch(m + skiplen, filename + skiplen, 0))\n \t\t\tgoto matched;\n \t\tif (ps_matched)\n \t\t\tps_matched++;\n@@ -80,7 +80,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)\n \tif (len >= ent->len)\n \t\tdie(\"git-ls-files: internal error - directory entry not superset of prefix\");\n \n-\tif (pathspec && !match(pathspec, ps_matched, ent->name, len))\n+\tif (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len))\n \t\treturn;\n \n \tfputs(tag, stdout);\n@@ -185,7 +185,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)\n \tif (len >= ce_namelen(ce))\n \t\tdie(\"git-ls-files: internal error - cache entry not superset of prefix\");\n \n-\tif (pathspec && !match(pathspec, ps_matched, ce->name, len))\n+\tif (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, len))\n \t\treturn;\n \n \tif (tag && *tag && show_valid_bit &&\n@@ -331,7 +331,7 @@ static const char *verify_pathspec(const char *prefix)\n  * that were given from the command line.  We are not\n  * going to write this index out.\n  */\n-static void overlay_tree(const char *tree_name, const char *prefix)\n+void overlay_tree_on_cache(const char *tree_name, const char *prefix)\n {\n \tstruct tree *tree;\n \tunsigned char sha1[20];\n@@ -384,6 +384,42 @@ static void overlay_tree(const char *tree_name, const char *prefix)\n \t}\n }\n \n+int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)\n+{\n+\t/*\n+\t * Make sure all pathspec matched; otherwise it is an error.\n+\t */\n+\tint num, errors = 0;\n+\tfor (num = 0; pathspec[num]; num++) {\n+\t\tint other, found_dup;\n+\n+\t\tif (ps_matched[num])\n+\t\t\tcontinue;\n+\t\t/*\n+\t\t * The caller might have fed identical pathspec\n+\t\t * twice.  Do not barf on such a mistake.\n+\t\t */\n+\t\tfor (found_dup = other = 0;\n+\t\t     !found_dup && pathspec[other];\n+\t\t     other++) {\n+\t\t\tif (other == num || !ps_matched[other])\n+\t\t\t\tcontinue;\n+\t\t\tif (!strcmp(pathspec[other], pathspec[num]))\n+\t\t\t\t/*\n+\t\t\t\t * Ok, we have a match already.\n+\t\t\t\t */\n+\t\t\t\tfound_dup = 1;\n+\t\t}\n+\t\tif (found_dup)\n+\t\t\tcontinue;\n+\n+\t\terror(\"pathspec '%s' did not match any file(s) known to git.\",\n+\t\t      pathspec[num] + prefix_offset);\n+\t\terrors++;\n+\t}\n+\treturn errors;\n+}\n+\n static const char ls_files_usage[] =\n \t\"git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* \"\n \t\"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] \"\n@@ -563,47 +599,17 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\t */\n \t\tif (show_stage || show_unmerged)\n \t\t\tdie(\"ls-files --with-tree is incompatible with -s or -u\");\n-\t\toverlay_tree(with_tree, prefix);\n+\t\toverlay_tree_on_cache(with_tree, prefix);\n \t}\n \tshow_files(&dir, prefix);\n \n \tif (ps_matched) {\n-\t\t/* We need to make sure all pathspec matched otherwise\n-\t\t * it is an error.\n-\t\t */\n-\t\tint num, errors = 0;\n-\t\tfor (num = 0; pathspec[num]; num++) {\n-\t\t\tint other, found_dup;\n-\n-\t\t\tif (ps_matched[num])\n-\t\t\t\tcontinue;\n-\t\t\t/*\n-\t\t\t * The caller might have fed identical pathspec\n-\t\t\t * twice.  Do not barf on such a mistake.\n-\t\t\t */\n-\t\t\tfor (found_dup = other = 0;\n-\t\t\t     !found_dup && pathspec[other];\n-\t\t\t     other++) {\n-\t\t\t\tif (other == num || !ps_matched[other])\n-\t\t\t\t\tcontinue;\n-\t\t\t\tif (!strcmp(pathspec[other], pathspec[num]))\n-\t\t\t\t\t/*\n-\t\t\t\t\t * Ok, we have a match already.\n-\t\t\t\t\t */\n-\t\t\t\t\tfound_dup = 1;\n-\t\t\t}\n-\t\t\tif (found_dup)\n-\t\t\t\tcontinue;\n-\n-\t\t\terror(\"pathspec '%s' did not match any file(s) known to git.\",\n-\t\t\t      pathspec[num] + prefix_offset);\n-\t\t\terrors++;\n-\t\t}\n-\n-\t\tif (errors)\n+\t\tint bad;\n+\t\tbad = report_path_error(ps_matched, pathspec, prefix_offset);\n+\t\tif (bad)\n \t\t\tfprintf(stderr, \"Did you forget to 'git add'?\\n\");\n \n-\t\treturn errors ? 1 : 0;\n+\t\treturn bad ? 1 : 0;\n \t}\n \n \treturn 0;\ndiff --git a/cache.h b/cache.h\nindex f0a25c7..a84f343 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -603,4 +603,10 @@ extern int diff_auto_refresh_index;\n /* match-trees.c */\n void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);\n \n+\n+/* ls-files */\n+int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);\n+int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);\n+void overlay_tree_on_cache(const char *tree_name, const char *prefix);\n+\n #endif /* CACHE_H */\n-- \n1.5.3.5.1815.g9445b\n"},{"id":"60221","messageId":"1195381287-26823-3-git-send-email-gitster@pobox.com","threadId":"10914","inReplyTo":"1195381287-26823-2-git-send-email-gitster@pobox.com","subject":"[PATCH] Fix add_files_to_cache() to take pathspec, not user specified list of files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-18T10:21:26Z","receivedAt":"2007-11-18T10:21:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This separates the logic to limit the extent of change to the\nindex by where you are (controlled by \"prefix\") and what you\nspecify from the command line (controlled by \"pathspec\").\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-add.c |    8 +++++---\n cache.h       |    4 +++-\n commit.h      |    1 -\n 3 files changed, 8 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin-add.c b/builtin-add.c\nindex 45b14e8..16f8557 100644\n--- a/builtin-add.c\n+++ b/builtin-add.c\n@@ -111,12 +111,12 @@ static void update_callback(struct diff_queue_struct *q,\n \t}\n }\n \n-void add_files_to_cache(int verbose, const char *prefix, const char **files)\n+void add_files_to_cache(int verbose, const char *prefix, const char **pathspec)\n {\n \tstruct rev_info rev;\n \tinit_revisions(&rev, prefix);\n \tsetup_revisions(0, NULL, &rev, NULL);\n-\trev.prune_data = get_pathspec(prefix, files);\n+\trev.prune_data = pathspec;\n \trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n \trev.diffopt.format_callback = update_callback;\n \trev.diffopt.format_callback_data = &verbose;\n@@ -198,9 +198,11 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \tnewfd = hold_locked_index(&lock_file, 1);\n \n \tif (take_worktree_changes) {\n+\t\tconst char **pathspec;\n \t\tif (read_cache() < 0)\n \t\t\tdie(\"index file corrupt\");\n-\t\tadd_files_to_cache(verbose, prefix, argv);\n+\t\tpathspec = get_pathspec(prefix, argv);\n+\t\tadd_files_to_cache(verbose, prefix, pathspec);\n \t\tgoto finish;\n \t}\n \ndiff --git a/cache.h b/cache.h\nindex a84f343..24e7b72 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -597,13 +597,15 @@ extern void trace_argv_printf(const char **argv, int count, const char *format,\n extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst);\n extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);\n \n+/* add */\n+void add_files_to_cache(int verbose, const char *prefix, const char **pathspec);\n+\n /* diff.c */\n extern int diff_auto_refresh_index;\n \n /* match-trees.c */\n void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);\n \n-\n /* ls-files */\n int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);\n int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);\ndiff --git a/commit.h b/commit.h\nindex 13b5372..e22aa77 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -132,7 +132,6 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,\n int in_merge_bases(struct commit *, struct commit **, int);\n \n extern int interactive_add(void);\n-extern void add_files_to_cache(int verbose, const char *prefix, const char **files);\n extern int rerere(void);\n \n #endif /* COMMIT_H */\n-- \n1.5.3.5.1815.g9445b\n"},{"id":"60222","messageId":"1195381287-26823-4-git-send-email-gitster@pobox.com","threadId":"10914","inReplyTo":"1195381287-26823-3-git-send-email-gitster@pobox.com","subject":"[PATCH] builtin-commit: fix partial-commit support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-18T10:21:27Z","receivedAt":"2007-11-18T10:21:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When making a partial-commit, we need to prepare two index\nfiles, one to be used to write out the tree to be committed\n(temporary index) and the other to be used as the index file\nafter the commit is made.\n\nThe temporary index needs to be initialized to HEAD and then all\nthe named paths on the command line need to be staged on top of\nthe index.  For this, running add_files_to_cache() that compares\nwhat is in the index and the paths given from the command line\nis not enough -- the comparison will miss the paths that the\nuser previously ran \"git add\" to the index since the HEAD\nbecause the index reset to the HEAD would not know about them.\nThe index file needs to get the same modification done when\npreparing the temporary index as described above.\n\nThis implementation mimics the behaviour of the scripted\nversion of git-commit.  It first runs overlay_tree_on_cache(),\nwhich was stolen from ls-files with the earlier change, to get\nthe list of paths that the user can potentially mean, and then\nuses pathspec_match() to find which ones the user meant.  This\nlist of paths is used to update both the temporary and the real\nindex file.\n\nAdditionally:\n\n - remove the temporary index file .git/next-index-* after\n   commit is done or aborted.\n\n - run post-commit hook with the real index file to be used\n   after the commit (previously it gave the temporary commit if\n   a partial commit was made).\n\n - resurrect the safety mechanism to refuse partial commits\n   during a merge to match the scripted version.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-commit.c |  245 +++++++++++++++++++++++++++++++++++++++++++++---------\n 1 files changed, 205 insertions(+), 40 deletions(-)\n\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 3e7d281..e487bc0 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -7,6 +7,7 @@\n \n #include \"cache.h\"\n #include \"cache-tree.h\"\n+#include \"dir.h\"\n #include \"builtin.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\n@@ -19,6 +20,7 @@\n #include \"strbuf.h\"\n #include \"utf8.h\"\n #include \"parse-options.h\"\n+#include \"path-list.h\"\n \n static const char * const builtin_commit_usage[] = {\n \t\"git-commit [options] [--] <filepattern>...\",\n@@ -28,7 +30,13 @@ static const char * const builtin_commit_usage[] = {\n static unsigned char head_sha1[20], merge_head_sha1[20];\n static char *use_message_buffer;\n static const char commit_editmsg[] = \"COMMIT_EDITMSG\";\n-static struct lock_file lock_file;\n+static struct lock_file index_lock; /* real index */\n+static struct lock_file false_lock; /* used only for partial commits */\n+static enum {\n+\tCOMMIT_AS_IS = 1,\n+\tCOMMIT_NORMAL,\n+\tCOMMIT_PARTIAL,\n+} commit_style;\n \n static char *logfile, *force_author, *template_file;\n static char *edit_message, *use_message;\n@@ -78,41 +86,182 @@ static struct option builtin_commit_options[] = {\n \tOPT_END()\n };\n \n+static void rollback_index_files(void)\n+{\n+\tswitch (commit_style) {\n+\tcase COMMIT_AS_IS:\n+\t\tbreak; /* nothing to do */\n+\tcase COMMIT_NORMAL:\n+\t\trollback_lock_file(&index_lock);\n+\t\tbreak;\n+\tcase COMMIT_PARTIAL:\n+\t\trollback_lock_file(&index_lock);\n+\t\trollback_lock_file(&false_lock);\n+\t\tbreak;\n+\t}\n+}\n+\n+static void commit_index_files(void)\n+{\n+\tswitch (commit_style) {\n+\tcase COMMIT_AS_IS:\n+\t\tbreak; /* nothing to do */\n+\tcase COMMIT_NORMAL:\n+\t\tcommit_lock_file(&index_lock);\n+\t\tbreak;\n+\tcase COMMIT_PARTIAL:\n+\t\tcommit_lock_file(&index_lock);\n+\t\trollback_lock_file(&false_lock);\n+\t\tbreak;\n+\t}\n+}\n+\n+/*\n+ * Take a union of paths in the index and the named tree (typically, \"HEAD\"),\n+ * and return the paths that match the given pattern in list.\n+ */\n+static int list_paths(struct path_list *list, const char *with_tree,\n+\t\t      const char *prefix, const char **pattern)\n+{\n+\tstruct dir_struct dir;\n+\tint i;\n+\tchar *m;\n+\n+\tfor (i = 0; pattern[i]; i++)\n+\t\t;\n+\tm = xcalloc(1, i);\n+\n+\tmemset(&dir, 0, sizeof(dir));\n+\tif (with_tree)\n+\t\toverlay_tree_on_cache(with_tree, prefix);\n+\n+\tfor (i = 0; i < active_nr; i++) {\n+\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tif (ce->ce_flags & htons(CE_UPDATE))\n+\t\t\tcontinue;\n+\t\tif (!pathspec_match(pattern, m, ce->name, 0))\n+\t\t\tcontinue;\n+\t\tif (excluded(&dir, ce->name))\n+\t\t\tcontinue;\n+\t\tpath_list_insert(ce->name, list);\n+\t}\n+\n+\treturn report_path_error(m, pattern, prefix ? strlen(prefix) : 0);\n+}\n+\n+static void add_remove_files(struct path_list *list)\n+{\n+\tint i;\n+\tfor (i = 0; i < list->nr; i++) {\n+\t\tstruct path_list_item *p = &(list->items[i]);\n+\t\tif (file_exists(p->path))\n+\t\t\tadd_file_to_cache(p->path, 0);\n+\t\telse\n+\t\t\tremove_file_from_cache(p->path);\n+\t}\n+}\n+\n static char *prepare_index(const char **files, const char *prefix)\n {\n \tint fd;\n \tstruct tree *tree;\n-\tstruct lock_file *next_index_lock;\n+\tstruct path_list partial;\n+\tconst char **pathspec = NULL;\n \n \tif (interactive) {\n \t\tinteractive_add();\n+\t\tcommit_style = COMMIT_NORMAL;\n \t\treturn get_index_file();\n \t}\n \n-\tfd = hold_locked_index(&lock_file, 1);\n \tif (read_cache() < 0)\n \t\tdie(\"index file corrupt\");\n \n-\tif (all || also) {\n-\t\tadd_files_to_cache(verbose, also ? prefix : NULL, files);\n+\tif (*files)\n+\t\tpathspec = get_pathspec(prefix, files);\n+\n+\t/*\n+\t * Non partial, non as-is commit.\n+\t *\n+\t * (1) get the real index;\n+\t * (2) update the_index as necessary;\n+\t * (3) write the_index out to the real index (still locked);\n+\t * (4) return the name of the locked index file.\n+\t *\n+\t * The caller should run hooks on the locked real index, and\n+\t * (A) if all goes well, commit the real index;\n+\t * (B) on failure, rollback the real index.\n+\t */\n+\tif (all || (also && pathspec && *pathspec)) {\n+\t\tint fd = hold_locked_index(&index_lock, 1);\n+\t\tadd_files_to_cache(0, also ? prefix : NULL, pathspec);\n \t\trefresh_cache(REFRESH_QUIET);\n \t\tif (write_cache(fd, active_cache, active_nr) || close(fd))\n \t\t\tdie(\"unable to write new_index file\");\n-\t\treturn lock_file.filename;\n+\t\tcommit_style = COMMIT_NORMAL;\n+\t\treturn index_lock.filename;\n \t}\n \n-\tif (*files == NULL) {\n-\t\t/* Commit index as-is. */\n-\t\trollback_lock_file(&lock_file);\n+\t/*\n+\t * As-is commit.\n+\t *\n+\t * (1) return the name of the real index file.\n+\t *\n+\t * The caller should run hooks on the real index, and run\n+\t * hooks on the real index, and create commit from the_index.\n+\t * We still need to refresh the index here.\n+\t */\n+\tif (!pathspec || !*pathspec) {\n+\t\tfd = hold_locked_index(&index_lock, 1);\n+\t\trefresh_cache(REFRESH_QUIET);\n+\t\tif (write_cache(fd, active_cache, active_nr) ||\n+\t\t    close(fd) || commit_locked_index(&index_lock))\n+\t\t\tdie(\"unable to write new_index file\");\n+\t\tcommit_style = COMMIT_AS_IS;\n \t\treturn get_index_file();\n \t}\n \n-\t/* update the user index file */\n-\tadd_files_to_cache(verbose, prefix, files);\n+\t/*\n+\t * A partial commit.\n+\t *\n+\t * (0) find the set of affected paths [NEEDSWORK]\n+\t * (1) get lock on the real index file;\n+\t * (2) update the_index with the given paths;\n+\t * (3) write the_index out to the real index (still locked);\n+\t * (4) get lock on the false index file;\n+\t * (5) reset the_index from HEAD, but keep the addition;\n+\t * (6) update the_index the same way as (2);\n+\t * (7) write the_index out to the false index file;\n+\t * (8) return the name of the false index file (still locked);\n+\t *\n+\t * The caller should run hooks on the locked false index, and\n+\t * (A) if all goes well, commit the real index;\n+\t * (B) on failure, rollback the real index;\n+\t * In either case, rollback the false index.\n+\t */\n+\tcommit_style = COMMIT_PARTIAL;\n+\n+\tif (file_exists(git_path(\"MERGE_HEAD\")))\n+\t\tdie(\"cannot do a partial commit during a merge.\");\n+\n+\tmemset(&partial, 0, sizeof(partial));\n+\tpartial.strdup_paths = 1;\n+\tif (list_paths(&partial, initial_commit ? NULL : \"HEAD\", prefix, pathspec))\n+\t\texit(1);\n+\n+\tdiscard_cache();\n+\tif (read_cache() < 0)\n+\t\tdie(\"cannot read the index\");\n+\n+\tfd = hold_locked_index(&index_lock, 1);\n+\tadd_remove_files(&partial);\n \trefresh_cache(REFRESH_QUIET);\n \tif (write_cache(fd, active_cache, active_nr) || close(fd))\n \t\tdie(\"unable to write new_index file\");\n \n+\tfd = hold_lock_file_for_update(&false_lock,\n+\t\t\t\t       git_path(\"next-index-%d\", getpid()), 1);\n+\tdiscard_cache();\n \tif (!initial_commit) {\n \t\ttree = parse_tree_indirect(head_sha1);\n \t\tif (!tree)\n@@ -120,17 +269,12 @@ static char *prepare_index(const char **files, const char *prefix)\n \t\tif (read_tree(tree, 0, NULL))\n \t\t\tdie(\"failed to read HEAD tree object\");\n \t}\n-\n-\t/* Use a lock file to garbage collect the temporary index file. */\n-\tnext_index_lock = xmalloc(sizeof(*next_index_lock));\n-\tfd = hold_lock_file_for_update(next_index_lock,\n-\t\t\t\t       git_path(\"next-index-%d\", getpid()), 1);\n-\tadd_files_to_cache(verbose, prefix, files);\n+\tadd_remove_files(&partial);\n \trefresh_cache(REFRESH_QUIET);\n-\tif (write_cache(fd, active_cache, active_nr) || close(fd))\n-\t\tdie(\"unable to write new_index file\");\n \n-\treturn next_index_lock->filename;\n+\tif (write_cache(fd, active_cache, active_nr) || close(fd))\n+\t\tdie(\"unable to write temporary index file\");\n+\treturn false_lock.filename;\n }\n \n static int run_status(FILE *fp, const char *index_file, const char *prefix)\n@@ -437,7 +581,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \n \tcommitable = run_status(stdout, index_file, prefix);\n \n-\trollback_lock_file(&lock_file);\n+\trollback_index_files();\n \n \treturn commitable ? 0 : 1;\n }\n@@ -527,23 +671,36 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \n \tindex_file = prepare_index(argv, prefix);\n \n-\tif (!no_verify && run_hook(index_file, \"pre-commit\", NULL))\n-\t\texit(1);\n+\tif (!no_verify && run_hook(index_file, \"pre-commit\", NULL)) {\n+\t\trollback_index_files();\n+\t\treturn 1;\n+\t}\n \n \tif (!prepare_log_message(index_file, prefix) && !in_merge) {\n \t\trun_status(stdout, index_file, prefix);\n+\t\trollback_index_files();\n \t\tunlink(commit_editmsg);\n \t\treturn 1;\n \t}\n \n-\tstrbuf_init(&sb, 0);\n-\n-\t/* Start building up the commit header */\n+\t/*\n+\t * Re-read the index as pre-commit hook could have updated it,\n+\t * and write it out as a tree.\n+\t */\n+\tdiscard_cache();\n \tread_cache_from(index_file);\n-\tactive_cache_tree = cache_tree();\n+\tif (!active_cache_tree)\n+\t\tactive_cache_tree = cache_tree();\n \tif (cache_tree_update(active_cache_tree,\n-\t\t\t      active_cache, active_nr, 0, 0) < 0)\n+\t\t\t      active_cache, active_nr, 0, 0) < 0) {\n+\t\trollback_index_files();\n \t\tdie(\"Error building trees\");\n+\t}\n+\n+\t/*\n+\t * The commit object\n+\t */\n+\tstrbuf_init(&sb, 0);\n \tstrbuf_addf(&sb, \"tree %s\\n\",\n \t\t    sha1_to_hex(active_cache_tree->sha1));\n \n@@ -592,20 +749,27 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \theader_len = sb.len;\n \tif (!no_edit)\n \t\tlaunch_editor(git_path(commit_editmsg), &sb);\n-\telse if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0)\n+\telse if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {\n+\t\trollback_index_files();\n \t\tdie(\"could not read commit message\\n\");\n-\tif (run_hook(index_file, \"commit-msg\", commit_editmsg))\n+\t}\n+\tif (run_hook(index_file, \"commit-msg\", commit_editmsg)) {\n+\t\trollback_index_files();\n \t\texit(1);\n+\t}\n \tstripspace(&sb, 1);\n-\tif (sb.len < header_len ||\n-\t    message_is_empty(&sb, header_len))\n+\tif (sb.len < header_len || message_is_empty(&sb, header_len)) {\n+\t\trollback_index_files();\n \t\tdie(\"* no commit message?  aborting commit.\");\n+\t}\n \tstrbuf_addch(&sb, '\\0');\n \tif (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf))\n \t\tfprintf(stderr, commit_utf8_warn);\n \n-\tif (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1))\n+\tif (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1)) {\n+\t\trollback_index_files();\n \t\tdie(\"failed to write commit object\");\n+\t}\n \n \tref_lock = lock_any_ref_for_update(\"HEAD\",\n \t\t\t\t\t   initial_commit ? NULL : head_sha1,\n@@ -620,21 +784,22 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tstrbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));\n \tstrbuf_insert(&sb, strlen(reflog_msg), \": \", 2);\n \n-\tif (!ref_lock)\n+\tif (!ref_lock) {\n+\t\trollback_index_files();\n \t\tdie(\"cannot lock HEAD ref\");\n-\tif (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0)\n+\t}\n+\tif (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) {\n+\t\trollback_index_files();\n \t\tdie(\"cannot update HEAD ref\");\n+\t}\n \n \tunlink(git_path(\"MERGE_HEAD\"));\n \tunlink(git_path(\"MERGE_MSG\"));\n \n-\tif (lock_file.filename[0] && commit_locked_index(&lock_file))\n-\t\tdie(\"failed to write new index\");\n+\tcommit_index_files();\n \n \trerere();\n-\n-\trun_hook(index_file, \"post-commit\", NULL);\n-\n+\trun_hook(get_index_file(), \"post-commit\", NULL);\n \tif (!quiet)\n \t\tprint_summary(prefix, commit_sha1);\n \n-- \n1.5.3.5.1815.g9445b\n"},{"id":"60225","messageId":"7vy7cvx30s.fsf@gitster.siamese.dyndns.org","threadId":"10914","inReplyTo":"1195381287-26823-4-git-send-email-gitster@pobox.com","subject":"Re: [PATCH] builtin-commit: fix partial-commit support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-18T10:26:59Z","receivedAt":"2007-11-18T10:26:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The above four are to be applied on top of kh/commit series,\n(without the WIP I sent out last night) on top of Dscho's\n\"Replace \"runstatus\" with \"status\" in the tests\" patch.\n\nWith these, all tests in t/ finally passes for me ;-)\n\nBut I am not a heavy user of partial commits nor commits from\nsubdirectories, so there may still be breakages.\n"},{"id":"60228","messageId":"7vr6inx1m8.fsf@gitster.siamese.dyndns.org","threadId":"10914","inReplyTo":"1195381287-26823-4-git-send-email-gitster@pobox.com","subject":"Re: [PATCH] builtin-commit: fix partial-commit support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-18T10:57:19Z","receivedAt":"2007-11-18T10:57:19Z","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> ...\n> Additionally:\n\n   - read the index file after pre-commit hook returns, as the\n     hook can modify it to affect the contents of the commit.\n\n>  - remove the temporary index file .git/next-index-* after\n>    commit is done or aborted.\n>\n>  - run post-commit hook with the real index file to be used\n>    after the commit (previously it gave the temporary commit if\n>    a partial commit was made).\n>\n>  - resurrect the safety mechanism to refuse partial commits\n>    during a merge to match the scripted version.\n\n>  static char *prepare_index(const char **files, const char *prefix)\n>  {\n>  \tint fd;\n>  \tstruct tree *tree;\n> -\tstruct lock_file *next_index_lock;\n> +\tstruct path_list partial;\n> +\tconst char **pathspec = NULL;\n>  \n>  \tif (interactive) {\n>  \t\tinteractive_add();\n> +\t\tcommit_style = COMMIT_NORMAL;\n\nThis should be COMMIT_AS_IS, to match the scripted version.\n\n> +\t/*\n> +\t * A partial commit.\n> +\t *\n> +\t * (0) find the set of affected paths [NEEDSWORK]\n\nThis [NEEDSWORK] has been resolved.\n\n> +\t * (1) get lock on the real index file;\n> +\t * (2) update the_index with the given paths;\n> +\t * (3) write the_index out to the real index (still locked);\n> +\t * (4) get lock on the false index file;\n> +\t * (5) reset the_index from HEAD, but keep the addition;\n\nThis \", but keep the addition\" is no longer necessary; the set\nof paths discovered in step (0) _will_ include the added paths\nthat are named by the user in **files.\n\n> +\t * (6) update the_index the same way as (2);\n> +\t * (7) write the_index out to the false index file;\n> +\t * (8) return the name of the false index file (still locked);\n> +\t *\n> +\t * The caller should run hooks on the locked false index, and\n\n ... \"create the commit using the false index\", of course.\n\n> +\t * (A) if all goes well, commit the real index;\n> +\t * (B) on failure, rollback the real index;\n> +\t * In either case, rollback the false index.\n> +\t */\n"},{"id":"60245","messageId":"7vve7zv1fd.fsf@gitster.siamese.dyndns.org","threadId":"10914","inReplyTo":"7vr6inx1m8.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] builtin-commit: fix partial-commit support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-18T18:44:22Z","receivedAt":"2007-11-18T18:44:22Z","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> diff --git a/builtin-commit.c b/builtin-commit.c\n> index 3e7d281..e487bc0 100644\n> --- a/builtin-commit.c\n> +++ b/builtin-commit.c\n\nThere was another...\n\n> +static int list_paths(struct path_list *list, const char *with_tree,\n> +\t\t      const char *prefix, const char **pattern)\n> +{\n> +\tstruct dir_struct dir;\n> +\tint i;\n> +\tchar *m;\n> +\n> +\tfor (i = 0; pattern[i]; i++)\n> +\t\t;\n> +\tm = xcalloc(1, i);\n> +\n> +\tmemset(&dir, 0, sizeof(dir));\n> +\tif (with_tree)\n> +\t\toverlay_tree_on_cache(with_tree, prefix);\n> +\n> +\tfor (i = 0; i < active_nr; i++) {\n> +\t\tstruct cache_entry *ce = active_cache[i];\n> +\t\tif (ce->ce_flags & htons(CE_UPDATE))\n> +\t\t\tcontinue;\n> +\t\tif (!pathspec_match(pattern, m, ce->name, 0))\n> +\t\t\tcontinue;\n> +\t\tif (excluded(&dir, ce->name))\n> +\t\t\tcontinue;\n> +\t\tpath_list_insert(ce->name, list);\n> +\t}\n\nThis \"excluded\", and the whole \"struct dir\" business is\nunneeded, as we are walking on the index and finding the set of\npaths the user cares about.\n"},{"id":"60331","messageId":"1195490855.16221.2.camel@hinata.boston.redhat.com","threadId":"10914","inReplyTo":"1195381287-26823-4-git-send-email-gitster@pobox.com","subject":"Re: [PATCH] builtin-commit: fix partial-commit support","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-11-19T16:47:35Z","receivedAt":"2007-11-19T16:47:35Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"On Sun, 2007-11-18 at 02:21 -0800, Junio C Hamano wrote:\n> When making a partial-commit, we need to prepare two index\n> files, one to be used to write out the tree to be committed\n> (temporary index) and the other to be used as the index file\n> after the commit is made.\n> \n> The temporary index needs to be initialized to HEAD and then all\n> the named paths on the command line need to be staged on top of\n> the index.  For this, running add_files_to_cache() that compares\n> what is in the index and the paths given from the command line\n> is not enough -- the comparison will miss the paths that the\n> user previously ran \"git add\" to the index since the HEAD\n> because the index reset to the HEAD would not know about them.\n> The index file needs to get the same modification done when\n> preparing the temporary index as described above.\n> \n> This implementation mimics the behaviour of the scripted\n> version of git-commit.  It first runs overlay_tree_on_cache(),\n> which was stolen from ls-files with the earlier change, to get\n> the list of paths that the user can potentially mean, and then\n> uses pathspec_match() to find which ones the user meant.  This\n> list of paths is used to update both the temporary and the real\n> index file.\n\nOh boy, this turns out to be much more involved than what I thought.\nThanks for fixing up this part of builtin-commit.\n\n> Additionally:\n> \n>  - remove the temporary index file .git/next-index-* after\n>    commit is done or aborted.\n\nHmm, I see the left-over next-index-* files too, but I though the lock\nfile would be cleaned up automatically by the atexit handler?\n\ncheers,\nKristian\n"}]}