{"thread":{"id":"16608","subject":"[PATCH 3/6] Make reset_tree() in builtin-checkout.c a bit more library-ish","startedAt":"2008-12-06T01:54:09Z","lastAt":"2008-12-06T02:08:35Z","messageCount":8,"participants":["Junio C Hamano","Linus Torvalds"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"97237","messageId":"1228528455-3089-1-git-send-email-gitster@pobox.com","threadId":"16608","inReplyTo":null,"subject":"[PATCH 0/6] Reusing \"checkout [-m]\" logic for \"reset --merge\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-12-06T01:54:09Z","receivedAt":"2008-12-06T01:54:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"checkout\" that switches the branches, and \"reset\" that updates the HEAD,\nhave some similarities.  Their major difference is that \"checkout\" affects\nwhich branch HEAD symref points at, while \"reset\" affects what commit such\na branch pointed at by the HEAD symref points at.  Interestingly, when\nyour HEAD is detached, they operate on the same thing.\n\nThey both change the commit pointed at by your HEAD from one commit to\nanother.  And what \"checkout\" and \"reset --merge\" do to the index and the\nwork tree while doing so are exactly the same.  Keep your local changes,\nwhile updating everything else from what is in the current commit to the\none we are switching to.\n\nThere is one difference, though.\n\n\"reset --merge\" does not have an equivalent of \"checkout -m\" that falls\nback to three-way merge to resolve the conflicts at the content level.\nThis series, which is still a WIP, is an attempt to do so.\n\nThere are few things to notice that this series doesn't do:\n\n - What \"reset --hard\" and \"checkout -f HEAD\" do to the index and work\n   tree (at least in naive thinking) ought to be the same.  This series\n   does not attempt to unify these.\n\n - \"reset --merge\" is left as posted by Linus, and it does only \"git\n   checkout\" equivalent, without \"-m\" (yet).\n\nThe latter can be enabled by changing a single line (see comment in\nreset_index_file() in [Patch 6/6]), but I haven't done so yet.\n\nWhile investigating for the former possibility, I noticed one interesting\nthing what \"checkout -f\" does *NOT* do.  Starting with an index with\nconflicts, \"git checkout -f HEAD\" keeps higher stage entries.\n\nFor example, you can insert \"exit\" before the \"D/F resolve\" test in t1004,\nrun the test (this leaves a handful higher stages in the index), go to the\ntrash directory and say \"git checkout -f HEAD\".\n\nIt leaves a single stage #1 entry (subdir/file2).  It probably is a bug in\nunpack-trees, but I didn't take a very deep look at it.\n\nJunio C Hamano (6):\n  builtin-checkout.c: check error return from read_cache()\n  read-cache.c: typofix in comment\n  Make reset_tree() in builtin-checkout.c a bit more library-ish\n  builtin-checkout.c: refactor merge_working_tree()\n  builtin-commit.c: further refactor branch switching\n  builtin-reset.c: use reset_index_and_worktree()\n\n builtin-checkout.c |  193 +++++++++++++++++++++++++++++-----------------------\n builtin-reset.c    |   66 +++++++++++++++++-\n read-cache.c       |    2 +-\n reset.h            |   11 +++\n 4 files changed, 183 insertions(+), 89 deletions(-)\n create mode 100644 reset.h\n"},{"id":"97238","messageId":"1228528455-3089-2-git-send-email-gitster@pobox.com","threadId":"16608","inReplyTo":"1228528455-3089-1-git-send-email-gitster@pobox.com","subject":"[PATCH 1/6] builtin-checkout.c: check error return from read_cache()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-12-06T01:54:10Z","receivedAt":"2008-12-06T01:54:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-checkout.c |    7 +++++--\n 1 files changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex 7f3bd7b..c2c0561 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -228,7 +228,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,\n \tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n \n \tnewfd = hold_locked_index(lock_file, 1);\n-\tread_cache();\n+\tif (read_cache() < 0)\n+\t\treturn error(\"corrupt index file\");\n \n \tif (source_tree)\n \t\tread_tree_some(source_tree, pathspec);\n@@ -371,7 +372,9 @@ static int merge_working_tree(struct checkout_opts *opts,\n \tint ret;\n \tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n \tint newfd = hold_locked_index(lock_file, 1);\n-\tread_cache();\n+\n+\tif (read_cache() < 0)\n+\t\treturn error(\"corrupt index file\");\n \n \tif (opts->force) {\n \t\tret = reset_tree(new->commit->tree, opts, 1);\n-- \n1.6.1.rc1.72.ga5ce6\n"},{"id":"97239","messageId":"1228528455-3089-3-git-send-email-gitster@pobox.com","threadId":"16608","inReplyTo":"1228528455-3089-2-git-send-email-gitster@pobox.com","subject":"[PATCH 2/6] read-cache.c: typofix in comment","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-12-06T01:54:11Z","receivedAt":"2008-12-06T01:54:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n read-cache.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/read-cache.c b/read-cache.c\nindex 22a8143..58d3f88 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1486,7 +1486,7 @@ int write_index(const struct index_state *istate, int newfd)\n \n /*\n  * Read the index file that is potentially unmerged into given\n- * index_state, dropping any unmerged entries.  Returns true is\n+ * index_state, dropping any unmerged entries.  Returns true if\n  * the index is unmerged.  Callers who want to refuse to work\n  * from an unmerged state can call this and check its return value,\n  * instead of calling read_cache().\n-- \n1.6.1.rc1.72.ga5ce6\n"},{"id":"97236","messageId":"1228528455-3089-4-git-send-email-gitster@pobox.com","threadId":"16608","inReplyTo":"1228528455-3089-3-git-send-email-gitster@pobox.com","subject":"[PATCH 3/6] Make reset_tree() in builtin-checkout.c a bit more library-ish","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-12-06T01:54:12Z","receivedAt":"2008-12-06T01:54:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The function demanded an internal structure \"struct checkout_opts\" as its\nargument, but we'd want to split this (and one of its callers) out into a\nseparate library.  This makes what the function wants a bit more explicit\nand much less dependent on the rest of builtin-checkout.c\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-checkout.c |   15 +++++++++------\n 1 files changed, 9 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex c2c0561..d88fce2 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -319,7 +319,7 @@ static void describe_detached_head(char *msg, struct commit *commit)\n \tstrbuf_release(&sb);\n }\n \n-static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree)\n+static int reset_tree(struct tree *tree, int quiet, int worktree, int *wt_error)\n {\n \tstruct unpack_trees_options opts;\n \tstruct tree_desc tree_desc;\n@@ -331,14 +331,14 @@ static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree)\n \topts.reset = 1;\n \topts.merge = 1;\n \topts.fn = oneway_merge;\n-\topts.verbose_update = !o->quiet;\n+\topts.verbose_update = !quiet;\n \topts.src_index = &the_index;\n \topts.dst_index = &the_index;\n \tparse_tree(tree);\n \tinit_tree_desc(&tree_desc, tree->buffer, tree->size);\n \tswitch (unpack_trees(1, &tree_desc, &opts)) {\n \tcase -2:\n-\t\to->writeout_error = 1;\n+\t\t*wt_error = 1;\n \t\t/*\n \t\t * We return 0 nevertheless, as the index is all right\n \t\t * and more importantly we have made best efforts to\n@@ -377,7 +377,8 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\treturn error(\"corrupt index file\");\n \n \tif (opts->force) {\n-\t\tret = reset_tree(new->commit->tree, opts, 1);\n+\t\tret = reset_tree(new->commit->tree, opts->quiet, 1,\n+\t\t\t\t &opts->writeout_error);\n \t\tif (ret)\n \t\t\treturn ret;\n \t} else {\n@@ -446,14 +447,16 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\t\to.verbosity = 0;\n \t\t\twork = write_tree_from_memory(&o);\n \n-\t\t\tret = reset_tree(new->commit->tree, opts, 1);\n+\t\t\tret = reset_tree(new->commit->tree, opts->quiet, 1,\n+\t\t\t\t\t &opts->writeout_error);\n \t\t\tif (ret)\n \t\t\t\treturn ret;\n \t\t\to.branch1 = new->name;\n \t\t\to.branch2 = \"local\";\n \t\t\tmerge_trees(&o, new->commit->tree, work,\n \t\t\t\told->commit->tree, &result);\n-\t\t\tret = reset_tree(new->commit->tree, opts, 0);\n+\t\t\tret = reset_tree(new->commit->tree, opts->quiet, 0,\n+\t\t\t\t\t &opts->writeout_error);\n \t\t\tif (ret)\n \t\t\t\treturn ret;\n \t\t}\n-- \n1.6.1.rc1.72.ga5ce6\n"},{"id":"97241","messageId":"1228528455-3089-5-git-send-email-gitster@pobox.com","threadId":"16608","inReplyTo":"1228528455-3089-4-git-send-email-gitster@pobox.com","subject":"[PATCH 4/6] builtin-checkout.c: refactor merge_working_tree()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-12-06T01:54:13Z","receivedAt":"2008-12-06T01:54:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The logic to bring the index and the working tree from one commit to\nanother is a nontrivial amount of code in this function.  Separate it out\ninto its own function, so that other callers can call it.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-checkout.c |  172 +++++++++++++++++++++++++++-------------------------\n 1 files changed, 89 insertions(+), 83 deletions(-)\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex d88fce2..9c45c49 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -352,6 +352,87 @@ static int reset_tree(struct tree *tree, int quiet, int worktree, int *wt_error)\n \t}\n }\n \n+static int switch_trees(int merge, int quiet, int *wt_error,\n+\t\t\tstruct commit *old_commit, const char *old_label,\n+\t\t\tstruct commit *new_commit, const char *new_label)\n+{\n+\tint ret;\n+\tstruct tree_desc trees[2];\n+\tstruct tree *tree;\n+\tstruct unpack_trees_options topts;\n+\n+\tmemset(&topts, 0, sizeof(topts));\n+\ttopts.head_idx = -1;\n+\ttopts.src_index = &the_index;\n+\ttopts.dst_index = &the_index;\n+\n+\ttopts.msgs.not_uptodate_file = \"You have local changes to '%s'; cannot switch branches.\";\n+\n+\trefresh_cache(REFRESH_QUIET);\n+\n+\tif (unmerged_cache()) {\n+\t\terror(\"you need to resolve your current index first\");\n+\t\treturn 1;\n+\t}\n+\n+\t/* 2-way merge to the new branch */\n+\ttopts.initial_checkout = is_cache_unborn();\n+\ttopts.update = 1;\n+\ttopts.merge = 1;\n+\ttopts.gently = merge;\n+\ttopts.verbose_update = !quiet;\n+\ttopts.fn = twoway_merge;\n+\ttopts.dir = xcalloc(1, sizeof(*topts.dir));\n+\ttopts.dir->show_ignored = 1;\n+\ttopts.dir->exclude_per_dir = \".gitignore\";\n+\ttree = parse_tree_indirect(old_commit->object.sha1);\n+\tinit_tree_desc(&trees[0], tree->buffer, tree->size);\n+\ttree = parse_tree_indirect(new_commit->object.sha1);\n+\tinit_tree_desc(&trees[1], tree->buffer, tree->size);\n+\n+\tret = unpack_trees(2, trees, &topts);\n+\tif (ret == -1) {\n+\t\t/*\n+\t\t * Unpack couldn't do a trivial merge; either give up\n+\t\t * or do a real merge, depending on whether the merge\n+\t\t * flag was used.\n+\t\t */\n+\t\tstruct tree *result;\n+\t\tstruct tree *work;\n+\t\tstruct merge_options o;\n+\t\tif (!merge)\n+\t\t\treturn 1;\n+\t\tparse_commit(old_commit);\n+\n+\t\t/* Do more real merge */\n+\n+\t\t/*\n+\t\t * We update the index fully, then write the tree from\n+\t\t * the index, then merge the new branch with the\n+\t\t * current tree, with the old branch as the base. Then\n+\t\t * we reset the index (but not the working tree) to\n+\t\t * the new branch, leaving the working tree as the\n+\t\t * merged version, but skipping unmerged entries in\n+\t\t * the index.\n+\t\t */\n+\n+\t\tadd_files_to_cache(NULL, NULL, 0);\n+\t\tinit_merge_options(&o);\n+\t\to.verbosity = 0;\n+\t\twork = write_tree_from_memory(&o);\n+\n+\t\tret = reset_tree(new_commit->tree, quiet, 1, wt_error);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\to.branch1 = new_label;\n+\t\to.branch2 = old_label;\n+\t\tmerge_trees(&o, new_commit->tree, work,\n+\t\t\t    old_commit->tree, &result);\n+\t\tret = reset_tree(new_commit->tree, quiet, 0, wt_error);\n+\t}\n+\treturn ret;\n+}\n+\n struct branch_info {\n \tconst char *name; /* The short name used */\n \tconst char *path; /* The full name of a real branch */\n@@ -376,91 +457,16 @@ static int merge_working_tree(struct checkout_opts *opts,\n \tif (read_cache() < 0)\n \t\treturn error(\"corrupt index file\");\n \n-\tif (opts->force) {\n+\tif (opts->force)\n \t\tret = reset_tree(new->commit->tree, opts->quiet, 1,\n \t\t\t\t &opts->writeout_error);\n-\t\tif (ret)\n-\t\t\treturn ret;\n-\t} else {\n-\t\tstruct tree_desc trees[2];\n-\t\tstruct tree *tree;\n-\t\tstruct unpack_trees_options topts;\n-\n-\t\tmemset(&topts, 0, sizeof(topts));\n-\t\ttopts.head_idx = -1;\n-\t\ttopts.src_index = &the_index;\n-\t\ttopts.dst_index = &the_index;\n-\n-\t\ttopts.msgs.not_uptodate_file = \"You have local changes to '%s'; cannot switch branches.\";\n-\n-\t\trefresh_cache(REFRESH_QUIET);\n-\n-\t\tif (unmerged_cache()) {\n-\t\t\terror(\"you need to resolve your current index first\");\n-\t\t\treturn 1;\n-\t\t}\n-\n-\t\t/* 2-way merge to the new branch */\n-\t\ttopts.initial_checkout = is_cache_unborn();\n-\t\ttopts.update = 1;\n-\t\ttopts.merge = 1;\n-\t\ttopts.gently = opts->merge;\n-\t\ttopts.verbose_update = !opts->quiet;\n-\t\ttopts.fn = twoway_merge;\n-\t\ttopts.dir = xcalloc(1, sizeof(*topts.dir));\n-\t\ttopts.dir->show_ignored = 1;\n-\t\ttopts.dir->exclude_per_dir = \".gitignore\";\n-\t\ttree = parse_tree_indirect(old->commit->object.sha1);\n-\t\tinit_tree_desc(&trees[0], tree->buffer, tree->size);\n-\t\ttree = parse_tree_indirect(new->commit->object.sha1);\n-\t\tinit_tree_desc(&trees[1], tree->buffer, tree->size);\n-\n-\t\tret = unpack_trees(2, trees, &topts);\n-\t\tif (ret == -1) {\n-\t\t\t/*\n-\t\t\t * Unpack couldn't do a trivial merge; either\n-\t\t\t * give up or do a real merge, depending on\n-\t\t\t * whether the merge flag was used.\n-\t\t\t */\n-\t\t\tstruct tree *result;\n-\t\t\tstruct tree *work;\n-\t\t\tstruct merge_options o;\n-\t\t\tif (!opts->merge)\n-\t\t\t\treturn 1;\n-\t\t\tparse_commit(old->commit);\n-\n-\t\t\t/* Do more real merge */\n-\n-\t\t\t/*\n-\t\t\t * We update the index fully, then write the\n-\t\t\t * tree from the index, then merge the new\n-\t\t\t * branch with the current tree, with the old\n-\t\t\t * branch as the base. Then we reset the index\n-\t\t\t * (but not the working tree) to the new\n-\t\t\t * branch, leaving the working tree as the\n-\t\t\t * merged version, but skipping unmerged\n-\t\t\t * entries in the index.\n-\t\t\t */\n-\n-\t\t\tadd_files_to_cache(NULL, NULL, 0);\n-\t\t\tinit_merge_options(&o);\n-\t\t\to.verbosity = 0;\n-\t\t\twork = write_tree_from_memory(&o);\n-\n-\t\t\tret = reset_tree(new->commit->tree, opts->quiet, 1,\n-\t\t\t\t\t &opts->writeout_error);\n-\t\t\tif (ret)\n-\t\t\t\treturn ret;\n-\t\t\to.branch1 = new->name;\n-\t\t\to.branch2 = \"local\";\n-\t\t\tmerge_trees(&o, new->commit->tree, work,\n-\t\t\t\told->commit->tree, &result);\n-\t\t\tret = reset_tree(new->commit->tree, opts->quiet, 0,\n-\t\t\t\t\t &opts->writeout_error);\n-\t\t\tif (ret)\n-\t\t\t\treturn ret;\n-\t\t}\n-\t}\n+\telse\n+\t\tret = switch_trees(opts->merge, opts->quiet,\n+\t\t\t\t   &opts->writeout_error,\n+\t\t\t\t   old->commit, \"local\",\n+\t\t\t\t   new->commit, new->name);\n+\tif (ret)\n+\t\treturn ret;\n \n \tif (write_cache(newfd, active_cache, active_nr) ||\n \t    commit_locked_index(lock_file))\n-- \n1.6.1.rc1.72.ga5ce6\n"},{"id":"97240","messageId":"1228528455-3089-6-git-send-email-gitster@pobox.com","threadId":"16608","inReplyTo":"1228528455-3089-5-git-send-email-gitster@pobox.com","subject":"[PATCH 5/6] builtin-commit.c: further refactor branch switching","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-12-06T01:54:14Z","receivedAt":"2008-12-06T01:54:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This adds reset_index_and_worktree() to further factor out the logic for\nswitching the index and the work tree state from one commit to another to\nshrink merge_working_tree() function.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-checkout.c |   24 ++++++++++++++++--------\n 1 files changed, 16 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex 9c45c49..a08941a 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -433,6 +433,18 @@ static int switch_trees(int merge, int quiet, int *wt_error,\n \treturn ret;\n }\n \n+int reset_index_and_worktree(int force, int merge, int quiet, int *wt_error,\n+\t\t\t     struct commit *old_commit, const char *old_label,\n+\t\t\t     struct commit *new_commit, const char *new_label)\n+{\n+\tif (force)\n+\t\treturn reset_tree(new_commit->tree, quiet, 1, wt_error);\n+\telse\n+\t\treturn switch_trees(merge, quiet, wt_error,\n+\t\t\t\t    old_commit, old_label,\n+\t\t\t\t    new_commit, new_label);\n+}\n+\n struct branch_info {\n \tconst char *name; /* The short name used */\n \tconst char *path; /* The full name of a real branch */\n@@ -457,14 +469,10 @@ static int merge_working_tree(struct checkout_opts *opts,\n \tif (read_cache() < 0)\n \t\treturn error(\"corrupt index file\");\n \n-\tif (opts->force)\n-\t\tret = reset_tree(new->commit->tree, opts->quiet, 1,\n-\t\t\t\t &opts->writeout_error);\n-\telse\n-\t\tret = switch_trees(opts->merge, opts->quiet,\n-\t\t\t\t   &opts->writeout_error,\n-\t\t\t\t   old->commit, \"local\",\n-\t\t\t\t   new->commit, new->name);\n+\tret = reset_index_and_worktree(opts->force, opts->merge, opts->quiet,\n+\t\t\t\t       &opts->writeout_error,\n+\t\t\t\t       old->commit, \"local\",\n+\t\t\t\t       new->commit, new->name);\n \tif (ret)\n \t\treturn ret;\n \n-- \n1.6.1.rc1.72.ga5ce6\n"},{"id":"97242","messageId":"1228528455-3089-7-git-send-email-gitster@pobox.com","threadId":"16608","inReplyTo":"1228528455-3089-6-git-send-email-gitster@pobox.com","subject":"[PATCH 6/6] builtin-reset.c: use reset_index_and_worktree()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-12-06T01:54:15Z","receivedAt":"2008-12-06T01:54:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This makes \"git reset --merge\" to use the same underlying mechanism \"git\ncheckout\" uses to update the index and the work tree.\n\nIt is possible to make it use the 3-way merge fallback \"git checkout -m\"\nallows, but this commit does not go there yet.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-checkout.c |    1 +\n builtin-reset.c    |   66 ++++++++++++++++++++++++++++++++++++++++++++++++++-\n reset.h            |   11 ++++++++\n 3 files changed, 76 insertions(+), 2 deletions(-)\n create mode 100644 reset.h\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex a08941a..d196521 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -16,6 +16,7 @@\n #include \"blob.h\"\n #include \"xdiff-interface.h\"\n #include \"ll-merge.h\"\n+#include \"reset.h\"\n \n static const char * const checkout_usage[] = {\n \t\"git checkout [options] <branch>\",\ndiff --git a/builtin-reset.c b/builtin-reset.c\nindex c0cb915..481a1cc 100644\n--- a/builtin-reset.c\n+++ b/builtin-reset.c\n@@ -18,6 +18,7 @@\n #include \"tree.h\"\n #include \"branch.h\"\n #include \"parse-options.h\"\n+#include \"reset.h\"\n \n static const char * const git_reset_usage[] = {\n \t\"git reset [--mixed | --soft | --hard | --merge] [-q] [<commit>]\",\n@@ -52,7 +53,7 @@ static inline int is_merge(void)\n \treturn !access(git_path(\"MERGE_HEAD\"), F_OK);\n }\n \n-static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet)\n+static int reset_index_file_via_read_tree(const unsigned char *sha1, int reset_type, int quiet)\n {\n \tint i = 0;\n \tconst char *args[6];\n@@ -77,6 +78,67 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet\n \treturn run_command_v_opt(args, RUN_GIT_CMD);\n }\n \n+static int reset_index_file(struct commit *new, int reset_type, int quiet)\n+{\n+\t/*\n+\t * SOFT reset won't even touch index nor work tree so\n+\t * this function is not called.\n+\t * MIXED updates the index only (should have been called\n+\t * --cached), and we let \"git read-tree\" to do so.\n+\t * HARD and MERGE corresponds to \"checkout -f\" and \"checkout [-m]\"\n+\t */\n+\tint merge, wt_error, ret;\n+\tstruct commit *old;\n+\tunsigned char head_sha1[20];\n+\tunsigned char *new_sha1 = new->object.sha1;\n+\tstruct lock_file *lock_file;\n+\tint newfd;\n+\n+\t/*\n+\t * We play lazy and let read-tree complain if HEAD is not\n+\t * readable.  Also on hard reset, HEAD does not have to be\n+\t * readable.\n+\t */\n+\tif (reset_type == MIXED ||\n+\t    reset_type == HARD ||\n+\t    get_sha1(\"HEAD\", head_sha1) ||\n+\t    !(old = lookup_commit_reference_gently(head_sha1, 1)))\n+\t\treturn reset_index_file_via_read_tree(new_sha1, reset_type,\n+\t\t\t\t\t\t      quiet);\n+\n+\tlock_file = xcalloc(1, sizeof(struct lock_file));\n+\tnewfd = hold_locked_index(lock_file, 1);\n+\tif (read_cache() < 0) {\n+\t\trollback_lock_file(lock_file);\n+\t\treturn reset_index_file_via_read_tree(new_sha1, reset_type,\n+\t\t\t\t\t\t      quiet);\n+\t}\n+\n+\t/*\n+\t * If we want \"checkout -m\" behaviour of falling back to\n+\t * the 3-way content level merges, we could use\n+\t *\n+\t *\t merge = (reset_type == MERGE);\n+\t *\n+\t * here.\n+\t */\n+\tmerge = 0;\n+\n+\twt_error = 0;\n+\tret = reset_index_and_worktree(0, merge, quiet, &wt_error,\n+\t\t\t\t       old, \"local\",\n+\t\t\t\t       new, \"reset\");\n+\tif (ret || wt_error) {\n+\t\trollback_lock_file(lock_file);\n+\t\treturn -1;\n+\t}\n+\n+\tif (write_cache(newfd, active_cache, active_nr) ||\n+\t    commit_locked_index(lock_file))\n+\t\treturn error(\"unable to write new index file\");\n+\treturn 0;\n+}\n+\n static void print_new_head_line(struct commit *commit)\n {\n \tconst char *hex, *body;\n@@ -276,7 +338,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \t\tif (is_merge() || read_cache() < 0 || unmerged_cache())\n \t\t\tdie(\"Cannot do a soft reset in the middle of a merge.\");\n \t}\n-\telse if (reset_index_file(sha1, reset_type, quiet))\n+\telse if (reset_index_file(commit, reset_type, quiet))\n \t\tdie(\"Could not reset index file to revision '%s'.\", rev);\n \n \t/* Any resets update HEAD to the head being switched to,\ndiff --git a/reset.h b/reset.h\nnew file mode 100644\nindex 0000000..9c42838\n--- /dev/null\n+++ b/reset.h\n@@ -0,0 +1,11 @@\n+#ifndef RESET_H\n+#define RESET_H\n+\n+extern int reset_index_and_worktree(int force, int merge, int quiet,\n+\t\t\t\t    int *wt_error,\n+\t\t\t\t    struct commit *old_commit,\n+\t\t\t\t    const char *old_label,\n+\t\t\t\t    struct commit *new_commit,\n+\t\t\t\t    const char *new_label);\n+\n+#endif\n-- \n1.6.1.rc1.72.ga5ce6\n"},{"id":"97244","messageId":"alpine.LFD.2.00.0812051802570.3425@nehalem.linux-foundation.org","threadId":"16608","inReplyTo":"1228528455-3089-6-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 5/6] builtin-commit.c: further refactor branch switching","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-12-06T02:08:35Z","receivedAt":"2008-12-06T02:08:35Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nThe whole series looks good, I think.\n\nBut one thing I reacted to is exemplified best by this one:\n\nOn Fri, 5 Dec 2008, Junio C Hamano wrote:\n>\n> +int reset_index_and_worktree(int force, int merge, int quiet, int *wt_error,\n> +\t\t\t     struct commit *old_commit, const char *old_label,\n> +\t\t\t     struct commit *new_commit, const char *new_label)\n\nThis takes three flags (force, merge, quiet) as three parameters.\n\nAnd in the series, 3/6 and 4/6 had other cases where the same flags (just \nnot _all_ of them) were passed around to other functions. In 4/6, you had \n\"switch_trees()\" taking \"int merge, int quiet\", and in 3/6 you had \n\"reset_tree()\" taking \"int quiet\" and also have a \"int worktree\" flag.\n\nI think it would be really nice to have a unified namespace for these \nflags. There are lots of commonalities between \"checkout\" and \"reset\" (and \ncertainly differences too), wouldn't it be nice to have something like\n\n\t#define CO_FORCE 0x001\n\t#define CO_MERGE 0x002\n\t#define CO_QUIET 0x004\n\t#define CO_WORKTREE 0x008\n\nand just pass in a single \"unsigned int checkout_flags\" as an argument?\n\nHaving eight parameters is obscene.\n\n\t\tLinus\n"}]}