{"thread":{"id":"49793","subject":"[PATCH/RFC] checkout: print something when checking out paths","startedAt":"2018-11-10T13:35:36Z","lastAt":"2019-03-11T14:27:47Z","messageCount":110,"participants":["Nguyễn Thái Ngọc Duy","Junio C Hamano","Duy Nguyen","Ævar Arnfjörð Bjarmason","Thomas Gummerer","Stefan Beller","Stefan Xenos","Dan Fabulich","Elijah Newren","Eric Sunshine","Eckhard Maaß"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"362891","messageId":"20181110133525.17538-1-pclouds@gmail.com","threadId":"49793","inReplyTo":null,"subject":"[PATCH/RFC] checkout: print something when checking out paths","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-10T13:35:25Z","receivedAt":"2018-11-10T13:35:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"One of the problems with \"git checkout\" is that it does so many\ndifferent things and could confuse people specially when we fail to\nhandle ambiguation correctly.\n\nOne way to help with that is tell the user what sort of operation is\nactually carried out. When switching branches, we always print\nsomething [1].  Checking out paths however is silent. Print something\nso that if we got the user intention wrong, they won't waste too much\ntime to find that out.\n\nSince the purpose of printing this is to help disambiguate. Only do it\nwhen \"--\" is missing (the actual reason though is many tests check\nempty stderr to see that no error is raised and I'm too lazy to fix\nall the test cases).\n\n[1] Knowing the number of paths updated could also be useful even in\n    normal case.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n This is related to another patch in\n \n https://public-inbox.org/git/20181110120707.25846-1-pclouds@gmail.com/T/#u\n\n While writing that patch I thought printing something would also\n help. But if we get ambiguation right (in that particular case) then\n we don't actually need this. But it still seems a good idea...\n\n apply.c                  |  3 ++-\n builtin/checkout-index.c |  6 ++++--\n builtin/checkout.c       | 30 ++++++++++++++++++++++--------\n builtin/difftool.c       |  2 +-\n cache.h                  |  4 ++--\n entry.c                  | 10 ++++++----\n unpack-trees.c           |  6 +++---\n 7 files changed, 40 insertions(+), 21 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 073d5f0451..5876b02197 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -3352,7 +3352,8 @@ static int checkout_target(struct index_state *istate,\n \n \tcostate.refresh_cache = 1;\n \tcostate.istate = istate;\n-\tif (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))\n+\tif (checkout_entry(ce, &costate, NULL, NULL) ||\n+\t    lstat(ce->name, st))\n \t\treturn error(_(\"cannot checkout %s\"), ce->name);\n \treturn 0;\n }\ndiff --git a/builtin/checkout-index.c b/builtin/checkout-index.c\nindex 88b86c8d9f..bada491f58 100644\n--- a/builtin/checkout-index.c\n+++ b/builtin/checkout-index.c\n@@ -67,7 +67,8 @@ static int checkout_file(const char *name, const char *prefix)\n \t\t\tcontinue;\n \t\tdid_checkout = 1;\n \t\tif (checkout_entry(ce, &state,\n-\t\t    to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)\n+\t\t\t\t   to_tempfile ? topath[ce_stage(ce)] : NULL,\n+\t\t\t\t   NULL) < 0)\n \t\t\terrs++;\n \t}\n \n@@ -111,7 +112,8 @@ static void checkout_all(const char *prefix, int prefix_length)\n \t\t\t\twrite_tempfile_record(last_ce->name, prefix);\n \t\t}\n \t\tif (checkout_entry(ce, &state,\n-\t\t    to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)\n+\t\t\t\t   to_tempfile ? topath[ce_stage(ce)] : NULL,\n+\t\t\t\t   NULL) < 0)\n \t\t\terrs++;\n \t\tlast_ce = ce;\n \t}\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex acdafc6e4c..13ed041dc1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -44,6 +44,7 @@ struct checkout_opts {\n \tint ignore_skipworktree;\n \tint ignore_other_worktrees;\n \tint show_progress;\n+\tint count_checkout_paths;\n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n \t * should be updated accordingly.\n@@ -165,12 +166,13 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)\n }\n \n static int checkout_stage(int stage, const struct cache_entry *ce, int pos,\n-\t\t\t  const struct checkout *state)\n+\t\t\t  const struct checkout *state, int *nr_checkouts)\n {\n \twhile (pos < active_nr &&\n \t       !strcmp(active_cache[pos]->name, ce->name)) {\n \t\tif (ce_stage(active_cache[pos]) == stage)\n-\t\t\treturn checkout_entry(active_cache[pos], state, NULL);\n+\t\t\treturn checkout_entry(active_cache[pos], state,\n+\t\t\t\t\t      NULL, nr_checkouts);\n \t\tpos++;\n \t}\n \tif (stage == 2)\n@@ -179,7 +181,7 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,\n \t\treturn error(_(\"path '%s' does not have their version\"), ce->name);\n }\n \n-static int checkout_merged(int pos, const struct checkout *state)\n+static int checkout_merged(int pos, const struct checkout *state, int *nr_checkouts)\n {\n \tstruct cache_entry *ce = active_cache[pos];\n \tconst char *path = ce->name;\n@@ -242,7 +244,7 @@ static int checkout_merged(int pos, const struct checkout *state)\n \tce = make_transient_cache_entry(mode, &oid, path, 2);\n \tif (!ce)\n \t\tdie(_(\"make_cache_entry failed for path '%s'\"), path);\n-\tstatus = checkout_entry(ce, state, NULL);\n+\tstatus = checkout_entry(ce, state, NULL, nr_checkouts);\n \tdiscard_cache_entry(ce);\n \treturn status;\n }\n@@ -257,6 +259,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tstruct commit *head;\n \tint errs = 0;\n \tstruct lock_file lock_file = LOCK_INIT;\n+\tint nr_checkouts = 0;\n \n \tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\tdie(_(\"'%s' cannot be used with updating paths\"), \"--track\");\n@@ -371,17 +374,27 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tstruct cache_entry *ce = active_cache[pos];\n \t\tif (ce->ce_flags & CE_MATCHED) {\n \t\t\tif (!ce_stage(ce)) {\n-\t\t\t\terrs |= checkout_entry(ce, &state, NULL);\n+\t\t\t\terrs |= checkout_entry(ce, &state,\n+\t\t\t\t\t\t       NULL, &nr_checkouts);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (opts->writeout_stage)\n-\t\t\t\terrs |= checkout_stage(opts->writeout_stage, ce, pos, &state);\n+\t\t\t\terrs |= checkout_stage(opts->writeout_stage,\n+\t\t\t\t\t\t       ce, pos,\n+\t\t\t\t\t\t       &state, &nr_checkouts);\n \t\t\telse if (opts->merge)\n-\t\t\t\terrs |= checkout_merged(pos, &state);\n+\t\t\t\terrs |= checkout_merged(pos, &state,\n+\t\t\t\t\t\t\t&nr_checkouts);\n \t\t\tpos = skip_same_name(ce, pos) - 1;\n \t\t}\n \t}\n-\terrs |= finish_delayed_checkout(&state);\n+\terrs |= finish_delayed_checkout(&state, &nr_checkouts);\n+\n+\tif (opts->count_checkout_paths)\n+\t\tfprintf_ln(stderr, Q_(\"%d path has been updated\",\n+\t\t\t\t      \"%d paths have been updated\",\n+\t\t\t\t      nr_checkouts),\n+\t\t\t   nr_checkouts);\n \n \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n@@ -1064,6 +1077,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\thas_dash_dash = 1; /* case (3) or (1) */\n \telse if (dash_dash_pos >= 2)\n \t\tdie(_(\"only one reference expected, %d given.\"), dash_dash_pos);\n+\topts->count_checkout_paths = !opts->quiet && !has_dash_dash;\n \n \tif (!strcmp(arg, \"-\"))\n \t\targ = \"@{-1}\";\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 544b0e8639..71318c26e1 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -323,7 +323,7 @@ static int checkout_path(unsigned mode, struct object_id *oid,\n \tint ret;\n \n \tce = make_transient_cache_entry(mode, oid, path, 0);\n-\tret = checkout_entry(ce, state, NULL);\n+\tret = checkout_entry(ce, state, NULL, NULL);\n \n \tdiscard_cache_entry(ce);\n \treturn ret;\ndiff --git a/cache.h b/cache.h\nindex 8b1ee42ae9..52fb6ba148 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1540,9 +1540,9 @@ struct checkout {\n #define CHECKOUT_INIT { NULL, \"\" }\n \n #define TEMPORARY_FILENAME_LENGTH 25\n-extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);\n+extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts);\n extern void enable_delayed_checkout(struct checkout *state);\n-extern int finish_delayed_checkout(struct checkout *state);\n+extern int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);\n \n struct cache_def {\n \tstruct strbuf path;\ndiff --git a/entry.c b/entry.c\nindex 5d136c5d55..5f213c30fe 100644\n--- a/entry.c\n+++ b/entry.c\n@@ -161,7 +161,7 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)\n \treturn !available;\n }\n \n-int finish_delayed_checkout(struct checkout *state)\n+int finish_delayed_checkout(struct checkout *state, int *nr_checkouts)\n {\n \tint errs = 0;\n \tunsigned delayed_object_count;\n@@ -226,7 +226,7 @@ int finish_delayed_checkout(struct checkout *state)\n \t\t\t\tce = index_file_exists(state->istate, path->string,\n \t\t\t\t\t\t       strlen(path->string), 0);\n \t\t\t\tif (ce) {\n-\t\t\t\t\terrs |= checkout_entry(ce, state, NULL);\n+\t\t\t\t\terrs |= checkout_entry(ce, state, NULL, nr_checkouts);\n \t\t\t\t\tfiltered_bytes += ce->ce_stat_data.sd_size;\n \t\t\t\t\tdisplay_throughput(progress, filtered_bytes);\n \t\t\t\t} else\n@@ -435,8 +435,8 @@ static void mark_colliding_entries(const struct checkout *state,\n  * its name is returned in topath[], which must be able to hold at\n  * least TEMPORARY_FILENAME_LENGTH bytes long.\n  */\n-int checkout_entry(struct cache_entry *ce,\n-\t\t   const struct checkout *state, char *topath)\n+int checkout_entry(struct cache_entry *ce, const struct checkout *state,\n+\t\t   char *topath, int *nr_checkouts)\n {\n \tstatic struct strbuf path = STRBUF_INIT;\n \tstruct stat st;\n@@ -506,5 +506,7 @@ int checkout_entry(struct cache_entry *ce,\n \t\treturn 0;\n \n \tcreate_directories(path.buf, path.len, state);\n+\tif (nr_checkouts)\n+\t\t(*nr_checkouts)++;\n \treturn write_entry(ce, path.buf, state, 0);\n }\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 7570df481b..17f1e601da 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -294,7 +294,7 @@ static void load_gitmodules_file(struct index_state *index,\n \t\t\trepo_read_gitmodules(the_repository);\n \t\t} else if (state && (ce->ce_flags & CE_UPDATE)) {\n \t\t\tsubmodule_free(the_repository);\n-\t\t\tcheckout_entry(ce, state, NULL);\n+\t\t\tcheckout_entry(ce, state, NULL, NULL);\n \t\t\trepo_read_gitmodules(the_repository);\n \t\t}\n \t}\n@@ -450,12 +450,12 @@ static int check_updates(struct unpack_trees_options *o)\n \t\t\tdisplay_progress(progress, ++cnt);\n \t\t\tce->ce_flags &= ~CE_UPDATE;\n \t\t\tif (o->update && !o->dry_run) {\n-\t\t\t\terrs |= checkout_entry(ce, &state, NULL);\n+\t\t\t\terrs |= checkout_entry(ce, &state, NULL, NULL);\n \t\t\t}\n \t\t}\n \t}\n \tstop_progress(&progress);\n-\terrs |= finish_delayed_checkout(&state);\n+\terrs |= finish_delayed_checkout(&state, NULL);\n \tif (o->update)\n \t\tgit_attr_set_direction(GIT_ATTR_CHECKIN);\n \n-- \n2.19.1.1231.g84aef82467\n\n"},{"id":"362966","messageId":"xmqq8t1y3jex.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"20181110133525.17538-1-pclouds@gmail.com","subject":"Re: [PATCH/RFC] checkout: print something when checking out paths","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-12T06:21:42Z","receivedAt":"2018-11-12T06:21:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Since the purpose of printing this is to help disambiguate. Only do it\n> when \"--\" is missing (the actual reason though is many tests check\n> empty stderr to see that no error is raised and I'm too lazy to fix\n> all the test cases).\n\nHeh, honesty is good but in this particular case the official reason\nalone would make perfect sense, too ;-)\n\nAs with progress output, shouldn't this automatically be turned off\nwhen the standard error stream goes to non tty, as the real purpose\nof printing is to help the user sitting in front of the terminal and\ninteracting with the command?\n\nAnd by this, I do not mean to say that \"When -- is missing\" can go\naway.  I agree that \"--\" is a clear sign that the user knows what\ns/he is doing---to overwrite the paths in the working tree that\nmatch the pathspec.\n\n> @@ -371,17 +374,27 @@ static int checkout_paths(const struct checkout_opts *opts,\n>  \t\tstruct cache_entry *ce = active_cache[pos];\n>  \t\tif (ce->ce_flags & CE_MATCHED) {\n>  \t\t\tif (!ce_stage(ce)) {\n> -\t\t\t\terrs |= checkout_entry(ce, &state, NULL);\n> +\t\t\t\terrs |= checkout_entry(ce, &state,\n> +\t\t\t\t\t\t       NULL, &nr_checkouts);\n>  \t\t\t\tcontinue;\n\nAs we count inside checkout_entry(), we might not actually write\nthis out when we know that the working tree file is up to date\nalready but we do not increment in that case either, so we keep\ntrack of the accurate count of \"updates\", not path matches (i.e. we\nare not reporting \"we made sure this many paths are up to date in\nthe working tree\"; instead we are reporting how many paths we needed\nto overwrite in the working tree).\n\n>  \t\t\t}\n>  \t\t\tif (opts->writeout_stage)\n> -\t\t\t\terrs |= checkout_stage(opts->writeout_stage, ce, pos, &state);\n> +\t\t\t\terrs |= checkout_stage(opts->writeout_stage,\n> +\t\t\t\t\t\t       ce, pos,\n> +\t\t\t\t\t\t       &state, &nr_checkouts);\n\nLikewike.\n\n>  \t\t\telse if (opts->merge)\n> -\t\t\t\terrs |= checkout_merged(pos, &state);\n> +\t\t\t\terrs |= checkout_merged(pos, &state,\n> +\t\t\t\t\t\t\t&nr_checkouts);\n\nLikewise.\n\n>  \t\t\tpos = skip_same_name(ce, pos) - 1;\n>  \t\t}\n>  \t}\n> -\terrs |= finish_delayed_checkout(&state);\n> +\terrs |= finish_delayed_checkout(&state, &nr_checkouts);\n> +\n> +\tif (opts->count_checkout_paths)\n> +\t\tfprintf_ln(stderr, Q_(\"%d path has been updated\",\n> +\t\t\t\t      \"%d paths have been updated\",\n> +\t\t\t\t      nr_checkouts),\n> +\t\t\t   nr_checkouts);\n\nThis one may want to be protected behind isatty(2).  Or the default\nvalue of count_checkout_paths could be tweakedbased on isatty(2).\n\n> @@ -1064,6 +1077,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n>  \t\thas_dash_dash = 1; /* case (3) or (1) */\n>  \telse if (dash_dash_pos >= 2)\n>  \t\tdie(_(\"only one reference expected, %d given.\"), dash_dash_pos);\n> +\topts->count_checkout_paths = !opts->quiet && !has_dash_dash;\n\ni.e. \"&& isatty(2)\" as well.\n\nOf course, \"--[no-]count-paths\" could be added to override this.\n"},{"id":"363060","messageId":"CACsJy8BGgf0J=iKNc3qmz_rTMNdaPmR_1v+9i3nhGKcuOH4AFA@mail.gmail.com","threadId":"49793","inReplyTo":"xmqq8t1y3jex.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH/RFC] checkout: print something when checking out paths","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-12T16:27:31Z","receivedAt":"2018-11-12T16:28:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Nov 12, 2018 at 7:21 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n> > Since the purpose of printing this is to help disambiguate. Only do it\n> > when \"--\" is missing (the actual reason though is many tests check\n> > empty stderr to see that no error is raised and I'm too lazy to fix\n> > all the test cases).\n>\n> Heh, honesty is good but in this particular case the official reason\n> alone would make perfect sense, too ;-)\n>\n> As with progress output, shouldn't this automatically be turned off\n> when the standard error stream goes to non tty, as the real purpose\n> of printing is to help the user sitting in front of the terminal and\n> interacting with the command?\n\nI see this at the same level as \"Switched to branch 'foo'\" which is\nalso protected by opts->quiet. If we start hiding messages based on\ntty it should be done for other messages in update_refs_for_switch()\ntoo, I guess?\n\n> And by this, I do not mean to say that \"When -- is missing\" can go\n> away.  I agree that \"--\" is a clear sign that the user knows what\n> s/he is doing---to overwrite the paths in the working tree that\n> match the pathspec.\n\nMy other problem with deciding to print based on the presence of \"--\"\nis also with branch switching code: it always prints something (unless\nit actually does nothing). So it's a bit strange that the\ncheckout_paths() behaves slightly different based on \"--\".\n\n\n> > @@ -371,17 +374,27 @@ static int checkout_paths(const struct checkout_opts *opts,\n> >               struct cache_entry *ce = active_cache[pos];\n> >               if (ce->ce_flags & CE_MATCHED) {\n> >                       if (!ce_stage(ce)) {\n> > -                             errs |= checkout_entry(ce, &state, NULL);\n> > +                             errs |= checkout_entry(ce, &state,\n> > +                                                    NULL, &nr_checkouts);\n> >                               continue;\n>\n> As we count inside checkout_entry(), we might not actually write\n> this out when we know that the working tree file is up to date\n> already but we do not increment in that case either, so we keep\n> track of the accurate count of \"updates\", not path matches (i.e. we\n> are not reporting \"we made sure this many paths are up to date in\n> the working tree\"; instead we are reporting how many paths we needed\n> to overwrite in the working tree).\n\nYeah. I counted matched paths first, but when you do \"git co .\", you\nget a huge (and meaningless, to me) number. Printing how many files\nare updated makes more sense.\n\n> >                       pos = skip_same_name(ce, pos) - 1;\n> >               }\n> >       }\n> > -     errs |= finish_delayed_checkout(&state);\n> > +     errs |= finish_delayed_checkout(&state, &nr_checkouts);\n> > +\n> > +     if (opts->count_checkout_paths)\n> > +             fprintf_ln(stderr, Q_(\"%d path has been updated\",\n> > +                                   \"%d paths have been updated\",\n> > +                                   nr_checkouts),\n> > +                        nr_checkouts);\n>\n> This one may want to be protected behind isatty(2).  Or the default\n> value of count_checkout_paths could be tweakedbased on isatty(2).\n\nAnother thing I'm going to change (if this v1 is in the right\ndirection): print different messages for \"git checkout -- foo\" and\n\"git checkout foo -- bar\". The first one is \"%d paths have been\nreverted\" but the second one does different things and probably should\nsay \"%d paths have been updated from branch foo\" or something like\nthat.\n-- \nDuy\n"},{"id":"363081","messageId":"xmqqy39yyruz.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CACsJy8BGgf0J=iKNc3qmz_rTMNdaPmR_1v+9i3nhGKcuOH4AFA@mail.gmail.com","subject":"Re: [PATCH/RFC] checkout: print something when checking out paths","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-12T20:15:48Z","receivedAt":"2018-11-12T20:15:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> Another thing I'm going to change (if this v1 is in the right\n> direction): print different messages for \"git checkout -- foo\" and\n> \"git checkout foo -- bar\". The first one is \"%d paths have been\n> reverted\" but the second one does different things and probably should\n> say \"%d paths have been updated from branch foo\" or something like\n> that.\n\nI actually think that it is a bad idea to deliberately use such\nmisleading words like \"revert\", that will discourage users from\nweaning themselves off of SVN.  \"checked out N paths out of the\nindex\", \"checked out N paths out of the commit X\" and \"checked out N\npaths out of branch B\" would be clear enough and won't have such a\nproblem---otherwise you are encouraging them to make a mistake\n\n\techo garbage >path\n\t... say oops ...\n\tgit revert path\n\n"},{"id":"363187","messageId":"20181113182800.26984-1-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181110133525.17538-1-pclouds@gmail.com","subject":"[PATCH v2] checkout: print something when checking out paths","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-13T18:28:00Z","receivedAt":"2018-11-13T18:28:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"One of the problems with \"git checkout\" is that it does so many\ndifferent things and could confuse people specially when we fail to\nhandle ambiguation correctly.\n\nOne way to help with that is tell the user what sort of operation is\nactually carried out. When switching branches, we always print\nsomething unless --quiet, either\n\n - \"HEAD is now at ...\"\n - \"Reset branch ...\"\n - \"Already on ...\"\n - \"Switched to and reset ...\"\n - \"Switched to a new branch ...\"\n - \"Switched to branch ...\"\n\nChecking out paths however is silent. Print something so that if we\ngot the user intention wrong, they won't waste too much time to find\nthat out. For the remaining cases of checkout we now print either\n\n - \"Checked out ... paths out of the index\"\n - \"Checked out ... paths out of <abbrev hash>\"\n\nSince the purpose of printing this is to help disambiguate. Only do it\nwhen \"--\" is missing.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n v2 updates the messages a bit but it does not check isatty or add\n --count-paths, for consistency reason with how messages are printed\n in the branch switching case.\n \n Consistency is not always a good reason to follow. But I haven't\n seen a strong reason to go against it.\n\n apply.c                  |  3 ++-\n builtin/checkout-index.c |  6 ++++--\n builtin/checkout.c       | 39 +++++++++++++++++++++++++++++++--------\n builtin/difftool.c       |  2 +-\n cache.h                  |  4 ++--\n entry.c                  | 10 ++++++----\n unpack-trees.c           |  6 +++---\n 7 files changed, 49 insertions(+), 21 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex 073d5f0451..5876b02197 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -3352,7 +3352,8 @@ static int checkout_target(struct index_state *istate,\n \n \tcostate.refresh_cache = 1;\n \tcostate.istate = istate;\n-\tif (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))\n+\tif (checkout_entry(ce, &costate, NULL, NULL) ||\n+\t    lstat(ce->name, st))\n \t\treturn error(_(\"cannot checkout %s\"), ce->name);\n \treturn 0;\n }\ndiff --git a/builtin/checkout-index.c b/builtin/checkout-index.c\nindex 88b86c8d9f..bada491f58 100644\n--- a/builtin/checkout-index.c\n+++ b/builtin/checkout-index.c\n@@ -67,7 +67,8 @@ static int checkout_file(const char *name, const char *prefix)\n \t\t\tcontinue;\n \t\tdid_checkout = 1;\n \t\tif (checkout_entry(ce, &state,\n-\t\t    to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)\n+\t\t\t\t   to_tempfile ? topath[ce_stage(ce)] : NULL,\n+\t\t\t\t   NULL) < 0)\n \t\t\terrs++;\n \t}\n \n@@ -111,7 +112,8 @@ static void checkout_all(const char *prefix, int prefix_length)\n \t\t\t\twrite_tempfile_record(last_ce->name, prefix);\n \t\t}\n \t\tif (checkout_entry(ce, &state,\n-\t\t    to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)\n+\t\t\t\t   to_tempfile ? topath[ce_stage(ce)] : NULL,\n+\t\t\t\t   NULL) < 0)\n \t\t\terrs++;\n \t\tlast_ce = ce;\n \t}\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex acdafc6e4c..3a0b86ec1c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -44,6 +44,7 @@ struct checkout_opts {\n \tint ignore_skipworktree;\n \tint ignore_other_worktrees;\n \tint show_progress;\n+\tint count_checkout_paths;\n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n \t * should be updated accordingly.\n@@ -165,12 +166,13 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)\n }\n \n static int checkout_stage(int stage, const struct cache_entry *ce, int pos,\n-\t\t\t  const struct checkout *state)\n+\t\t\t  const struct checkout *state, int *nr_checkouts)\n {\n \twhile (pos < active_nr &&\n \t       !strcmp(active_cache[pos]->name, ce->name)) {\n \t\tif (ce_stage(active_cache[pos]) == stage)\n-\t\t\treturn checkout_entry(active_cache[pos], state, NULL);\n+\t\t\treturn checkout_entry(active_cache[pos], state,\n+\t\t\t\t\t      NULL, nr_checkouts);\n \t\tpos++;\n \t}\n \tif (stage == 2)\n@@ -179,7 +181,7 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,\n \t\treturn error(_(\"path '%s' does not have their version\"), ce->name);\n }\n \n-static int checkout_merged(int pos, const struct checkout *state)\n+static int checkout_merged(int pos, const struct checkout *state, int *nr_checkouts)\n {\n \tstruct cache_entry *ce = active_cache[pos];\n \tconst char *path = ce->name;\n@@ -242,7 +244,7 @@ static int checkout_merged(int pos, const struct checkout *state)\n \tce = make_transient_cache_entry(mode, &oid, path, 2);\n \tif (!ce)\n \t\tdie(_(\"make_cache_entry failed for path '%s'\"), path);\n-\tstatus = checkout_entry(ce, state, NULL);\n+\tstatus = checkout_entry(ce, state, NULL, nr_checkouts);\n \tdiscard_cache_entry(ce);\n \treturn status;\n }\n@@ -257,6 +259,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tstruct commit *head;\n \tint errs = 0;\n \tstruct lock_file lock_file = LOCK_INIT;\n+\tint nr_checkouts = 0;\n \n \tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\tdie(_(\"'%s' cannot be used with updating paths\"), \"--track\");\n@@ -371,17 +374,36 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tstruct cache_entry *ce = active_cache[pos];\n \t\tif (ce->ce_flags & CE_MATCHED) {\n \t\t\tif (!ce_stage(ce)) {\n-\t\t\t\terrs |= checkout_entry(ce, &state, NULL);\n+\t\t\t\terrs |= checkout_entry(ce, &state,\n+\t\t\t\t\t\t       NULL, &nr_checkouts);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (opts->writeout_stage)\n-\t\t\t\terrs |= checkout_stage(opts->writeout_stage, ce, pos, &state);\n+\t\t\t\terrs |= checkout_stage(opts->writeout_stage,\n+\t\t\t\t\t\t       ce, pos,\n+\t\t\t\t\t\t       &state, &nr_checkouts);\n \t\t\telse if (opts->merge)\n-\t\t\t\terrs |= checkout_merged(pos, &state);\n+\t\t\t\terrs |= checkout_merged(pos, &state,\n+\t\t\t\t\t\t\t&nr_checkouts);\n \t\t\tpos = skip_same_name(ce, pos) - 1;\n \t\t}\n \t}\n-\terrs |= finish_delayed_checkout(&state);\n+\terrs |= finish_delayed_checkout(&state, &nr_checkouts);\n+\n+\tif (opts->count_checkout_paths) {\n+\t\tif (opts->source_tree)\n+\t\t\tfprintf_ln(stderr, Q_(\"Checked out %d path out of %s\",\n+\t\t\t\t\t      \"Checked out %d paths out of %s\",\n+\t\t\t\t\t      nr_checkouts),\n+\t\t\t\t   nr_checkouts,\n+\t\t\t\t   find_unique_abbrev(&opts->source_tree->object.oid,\n+\t\t\t\t\t\t      DEFAULT_ABBREV));\n+\t\telse\n+\t\t\tfprintf_ln(stderr, Q_(\"Checked out %d path out of the index\",\n+\t\t\t\t\t      \"Checked out %d paths out of the index\",\n+\t\t\t\t\t      nr_checkouts),\n+\t\t\t\t   nr_checkouts);\n+\t}\n \n \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n@@ -1064,6 +1086,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\thas_dash_dash = 1; /* case (3) or (1) */\n \telse if (dash_dash_pos >= 2)\n \t\tdie(_(\"only one reference expected, %d given.\"), dash_dash_pos);\n+\topts->count_checkout_paths = !opts->quiet && !has_dash_dash;\n \n \tif (!strcmp(arg, \"-\"))\n \t\targ = \"@{-1}\";\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 544b0e8639..71318c26e1 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -323,7 +323,7 @@ static int checkout_path(unsigned mode, struct object_id *oid,\n \tint ret;\n \n \tce = make_transient_cache_entry(mode, oid, path, 0);\n-\tret = checkout_entry(ce, state, NULL);\n+\tret = checkout_entry(ce, state, NULL, NULL);\n \n \tdiscard_cache_entry(ce);\n \treturn ret;\ndiff --git a/cache.h b/cache.h\nindex 8b1ee42ae9..52fb6ba148 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1540,9 +1540,9 @@ struct checkout {\n #define CHECKOUT_INIT { NULL, \"\" }\n \n #define TEMPORARY_FILENAME_LENGTH 25\n-extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);\n+extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts);\n extern void enable_delayed_checkout(struct checkout *state);\n-extern int finish_delayed_checkout(struct checkout *state);\n+extern int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);\n \n struct cache_def {\n \tstruct strbuf path;\ndiff --git a/entry.c b/entry.c\nindex 5d136c5d55..5f213c30fe 100644\n--- a/entry.c\n+++ b/entry.c\n@@ -161,7 +161,7 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)\n \treturn !available;\n }\n \n-int finish_delayed_checkout(struct checkout *state)\n+int finish_delayed_checkout(struct checkout *state, int *nr_checkouts)\n {\n \tint errs = 0;\n \tunsigned delayed_object_count;\n@@ -226,7 +226,7 @@ int finish_delayed_checkout(struct checkout *state)\n \t\t\t\tce = index_file_exists(state->istate, path->string,\n \t\t\t\t\t\t       strlen(path->string), 0);\n \t\t\t\tif (ce) {\n-\t\t\t\t\terrs |= checkout_entry(ce, state, NULL);\n+\t\t\t\t\terrs |= checkout_entry(ce, state, NULL, nr_checkouts);\n \t\t\t\t\tfiltered_bytes += ce->ce_stat_data.sd_size;\n \t\t\t\t\tdisplay_throughput(progress, filtered_bytes);\n \t\t\t\t} else\n@@ -435,8 +435,8 @@ static void mark_colliding_entries(const struct checkout *state,\n  * its name is returned in topath[], which must be able to hold at\n  * least TEMPORARY_FILENAME_LENGTH bytes long.\n  */\n-int checkout_entry(struct cache_entry *ce,\n-\t\t   const struct checkout *state, char *topath)\n+int checkout_entry(struct cache_entry *ce, const struct checkout *state,\n+\t\t   char *topath, int *nr_checkouts)\n {\n \tstatic struct strbuf path = STRBUF_INIT;\n \tstruct stat st;\n@@ -506,5 +506,7 @@ int checkout_entry(struct cache_entry *ce,\n \t\treturn 0;\n \n \tcreate_directories(path.buf, path.len, state);\n+\tif (nr_checkouts)\n+\t\t(*nr_checkouts)++;\n \treturn write_entry(ce, path.buf, state, 0);\n }\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 7570df481b..17f1e601da 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -294,7 +294,7 @@ static void load_gitmodules_file(struct index_state *index,\n \t\t\trepo_read_gitmodules(the_repository);\n \t\t} else if (state && (ce->ce_flags & CE_UPDATE)) {\n \t\t\tsubmodule_free(the_repository);\n-\t\t\tcheckout_entry(ce, state, NULL);\n+\t\t\tcheckout_entry(ce, state, NULL, NULL);\n \t\t\trepo_read_gitmodules(the_repository);\n \t\t}\n \t}\n@@ -450,12 +450,12 @@ static int check_updates(struct unpack_trees_options *o)\n \t\t\tdisplay_progress(progress, ++cnt);\n \t\t\tce->ce_flags &= ~CE_UPDATE;\n \t\t\tif (o->update && !o->dry_run) {\n-\t\t\t\terrs |= checkout_entry(ce, &state, NULL);\n+\t\t\t\terrs |= checkout_entry(ce, &state, NULL, NULL);\n \t\t\t}\n \t\t}\n \t}\n \tstop_progress(&progress);\n-\terrs |= finish_delayed_checkout(&state);\n+\terrs |= finish_delayed_checkout(&state, NULL);\n \tif (o->update)\n \t\tgit_attr_set_direction(GIT_ATTR_CHECKIN);\n \n-- \n2.19.1.1318.g5295c6727d\n\n"},{"id":"363342","messageId":"xmqqo9asm0hr.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"20181113182800.26984-1-pclouds@gmail.com","subject":"Re: [PATCH v2] checkout: print something when checking out paths","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-14T10:12:16Z","receivedAt":"2018-11-14T10:12:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> One of the problems with \"git checkout\" is that it does so many\n> different things and could confuse people specially when we fail to\n> handle ambiguation correctly.\n\nYou would have realized that this is way too noisy if you ran \"make\ntest\", which may have spewed something like this on the screen.\n\n[19:09:19] t4120-apply-popt.sh ................................ ok     1624 ms ( 0.26 usr  0.21 sys +  5.31 cusr  3.51 csys =  9.29 CPU)\n[19:09:20] t9164-git-svn-dcommit-concurrent.sh ................ skipped: Perl SVN libraries not found or unusable\n[19:09:20] t1310-config-default.sh ............................ ok      177 ms ( 0.07 usr  0.01 sys +  0.89 cusr  0.66 csys =  1.63 CPU)\n===(   20175;154  1297/?  155/?  6/?  3/3  2/?  4/?  4/?  3/?  5... )===Checked out 1 path out of the index\nChecked out 1 path out of the index\nChecked out 1 path out of the index\nChecked out 1 path out of the index\nChecked out 1 path out of the index\n[19:09:20] t1408-packed-refs.sh ............................... ok      310 ms ( 0.06 usr  0.00 sys +  0.69 cusr  0.52 csys =  1.27 CPU)\n[19:09:20] t0025-crlf-renormalize.sh .......................... ok      246 ms ( 0.03 usr  0.01 sys +  0.34 cusr  0.22 csys =  0.60 CPU)\n\nI am very tempted to suggest to treat this as a training wheel and\nenable only when checkout.showpathcount is set to true, or something\nlike that.\n"},{"id":"363373","messageId":"CACsJy8C663xYVkb8TZVVe-Hwv_V4oO4mJP0UFv4+nfRTX5gzqA@mail.gmail.com","threadId":"49793","inReplyTo":"xmqqo9asm0hr.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2] checkout: print something when checking out paths","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-14T15:31:15Z","receivedAt":"2018-11-14T15:31:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Nov 14, 2018 at 11:12 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n> > One of the problems with \"git checkout\" is that it does so many\n> > different things and could confuse people specially when we fail to\n> > handle ambiguation correctly.\n>\n> You would have realized that this is way too noisy if you ran \"make\n> test\", which may have spewed something like this on the screen.\n\nOh I realize it because it's part of my git build and I often use \"git\nco <paths>\". I'm just telling (or kidding?) myself that I'm just so\nused to the old behavior and may need some time to feel comfortable\nwith the new one.\n\n> [19:09:19] t4120-apply-popt.sh ................................ ok     1624 ms ( 0.26 usr  0.21 sys +  5.31 cusr  3.51 csys =  9.29 CPU)\n> [19:09:20] t9164-git-svn-dcommit-concurrent.sh ................ skipped: Perl SVN libraries not found or unusable\n> [19:09:20] t1310-config-default.sh ............................ ok      177 ms ( 0.07 usr  0.01 sys +  0.89 cusr  0.66 csys =  1.63 CPU)\n> ===(   20175;154  1297/?  155/?  6/?  3/3  2/?  4/?  4/?  3/?  5... )===Checked out 1 path out of the index\n> Checked out 1 path out of the index\n> Checked out 1 path out of the index\n> Checked out 1 path out of the index\n> Checked out 1 path out of the index\n> [19:09:20] t1408-packed-refs.sh ............................... ok      310 ms ( 0.06 usr  0.00 sys +  0.69 cusr  0.52 csys =  1.27 CPU)\n> [19:09:20] t0025-crlf-renormalize.sh .......................... ok      246 ms ( 0.03 usr  0.01 sys +  0.34 cusr  0.22 csys =  0.60 CPU)\n>\n> I am very tempted to suggest to treat this as a training wheel and\n> enable only when checkout.showpathcount is set to true, or something\n> like that.\n\nMaybe we just drop it then. I'm not adding a training wheel. I'm\ntrying to make this complex command safer somewhat. But maybe this is\na wrong direction. I'll give the idea \"switch-branch / restore-path\nalternative commands\" a go some time. Then the new generation can just\nstick to those and old timers stay with \"git checkout\".\n-- \nDuy\n"},{"id":"363633","messageId":"8736rx1ah9.fsf@evledraar.gmail.com","threadId":"49793","inReplyTo":"CACsJy8BGgf0J=iKNc3qmz_rTMNdaPmR_1v+9i3nhGKcuOH4AFA@mail.gmail.com","subject":"Re: [PATCH/RFC] checkout: print something when checking out paths","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-11-19T13:08:02Z","receivedAt":"2018-11-19T13:08:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Nov 12 2018, Duy Nguyen wrote:\n\n> On Mon, Nov 12, 2018 at 7:21 AM Junio C Hamano <gitster@pobox.com> wrote:\n>>\n>> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>>\n>> > Since the purpose of printing this is to help disambiguate. Only do it\n>> > when \"--\" is missing (the actual reason though is many tests check\n>> > empty stderr to see that no error is raised and I'm too lazy to fix\n>> > all the test cases).\n>>\n>> Heh, honesty is good but in this particular case the official reason\n>> alone would make perfect sense, too ;-)\n>>\n>> As with progress output, shouldn't this automatically be turned off\n>> when the standard error stream goes to non tty, as the real purpose\n>> of printing is to help the user sitting in front of the terminal and\n>> interacting with the command?\n>\n> I see this at the same level as \"Switched to branch 'foo'\" which is\n> also protected by opts->quiet. If we start hiding messages based on\n> tty it should be done for other messages in update_refs_for_switch()\n> too, I guess?\n\nI have no real opinion either way, but whatever we can do about\n\"checkout\" being so confusing since it does so many things is most\nwelcome.\n\nJust an alternative: Maybe we can start this out as advice() output\nthat's either opt-in via config (not on by default) to start with, or\nhave some advice_tty() that only emits it in the same circumstances we\nemit the progress output?\n"},{"id":"363639","messageId":"CACsJy8B6wKGg2Jsopct-0dYNhKJGf9RdnrnTqBOt4kxy6LzxMw@mail.gmail.com","threadId":"49793","inReplyTo":"8736rx1ah9.fsf@evledraar.gmail.com","subject":"Re: [PATCH/RFC] checkout: print something when checking out paths","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-19T15:19:53Z","receivedAt":"2018-11-19T15:20:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Nov 19, 2018 at 2:08 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n>\n> On Mon, Nov 12 2018, Duy Nguyen wrote:\n>\n> > On Mon, Nov 12, 2018 at 7:21 AM Junio C Hamano <gitster@pobox.com> wrote:\n> >>\n> >> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n> >>\n> >> > Since the purpose of printing this is to help disambiguate. Only do it\n> >> > when \"--\" is missing (the actual reason though is many tests check\n> >> > empty stderr to see that no error is raised and I'm too lazy to fix\n> >> > all the test cases).\n> >>\n> >> Heh, honesty is good but in this particular case the official reason\n> >> alone would make perfect sense, too ;-)\n> >>\n> >> As with progress output, shouldn't this automatically be turned off\n> >> when the standard error stream goes to non tty, as the real purpose\n> >> of printing is to help the user sitting in front of the terminal and\n> >> interacting with the command?\n> >\n> > I see this at the same level as \"Switched to branch 'foo'\" which is\n> > also protected by opts->quiet. If we start hiding messages based on\n> > tty it should be done for other messages in update_refs_for_switch()\n> > too, I guess?\n>\n> I have no real opinion either way, but whatever we can do about\n> \"checkout\" being so confusing since it does so many things is most\n> welcome.\n>\n> Just an alternative: Maybe we can start this out as advice() output\n> that's either opt-in via config (not on by default) to start with, or\n> have some advice_tty() that only emits it in the same circumstances we\n> emit the progress output?\n\nNo let's drop this for now. I promise to come back with something\nbetter (at least it still sounds better in my mind). If that idea does\nnot work out, we can come back and see if we can improve this.\n-- \nDuy\n"},{"id":"363694","messageId":"xmqqpnv0bgsd.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CACsJy8B6wKGg2Jsopct-0dYNhKJGf9RdnrnTqBOt4kxy6LzxMw@mail.gmail.com","subject":"Re: [PATCH/RFC] checkout: print something when checking out paths","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-20T02:53:54Z","receivedAt":"2018-11-20T02:53:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> > I see this at the same level as \"Switched to branch 'foo'\" which is\n>> > also protected by opts->quiet. If we start hiding messages based on\n>> > tty it should be done for other messages in update_refs_for_switch()\n>> > too, I guess?\n>\n> No let's drop this for now. I promise to come back with something\n> better (at least it still sounds better in my mind). If that idea does\n> not work out, we can come back and see if we can improve this.\n\nLet's leave it in 'pu'.\n\nI do agree that this is similar to existing messages that talk about\ncheckout out a branch to work on etc., and I think giving feedback\nwhen checkout paths out _is_ a good thing to do for interactive\nusers.\n\nIf we were to squelch such \"notice\" output for non-interactive use,\nwe should do so to the \"notice\" messages for checking out a branch,\nas well, and also to other subcommands that report what they did\nwith these \"notice\" output.  And that is a separate topic.\n\nThe primary reason why I was annoyed was because \"make test\" (I\nthink I have DEFAULT_TEST_TARGET=prove, if it matters) output was\nlittered with these \"checked out N paths\", even though I am not\nasking for verbose output.  \n\nIt could be that the real cause of that is perhaps because we have\ntoo many \"git checkout\" that is outside test_expect_* block, in\nwhich case we should fix these tests to perform the checkout inside\na test_expect_success block for test set-up, and my annoyance was\nonly shooting at the messenger.\n\nFor example, the attached patch illustrates the right problem but\naddresses it in a wrong way.  This checkout_files() helper does too\nmany things outside (and before) the test_expect_success block it\nhas (other helpers like commit_chk_wrnNNO share the same problem),\nand making \"git checkout\" noisy will reveal that as a problem by\nleaking the noisy output directly to the tester.  But the real fix\nis to enclose the set-up step inside a test_expect_success block,\nwhich is not done by the following illustration patch, which instead\njust squelches the message.\n\n t/t0027-auto-crlf.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh\nindex beb5927f77..3587e454f1 100755\n--- a/t/t0027-auto-crlf.sh\n+++ b/t/t0027-auto-crlf.sh\n@@ -293,9 +293,9 @@ checkout_files () {\n \tdo\n \t\trm crlf_false_attr__$f.txt &&\n \t\tif test -z \"$ceol\"; then\n-\t\t\tgit checkout crlf_false_attr__$f.txt\n+\t\t\tgit checkout -- crlf_false_attr__$f.txt\n \t\telse\n-\t\t\tgit -c core.eol=$ceol checkout crlf_false_attr__$f.txt\n+\t\t\tgit -c core.eol=$ceol checkout -- crlf_false_attr__$f.txt\n \t\tfi\n \tdone\n \n\n"},{"id":"363759","messageId":"20181120174554.GA29910@duynguyen.home","threadId":"49793","inReplyTo":"CACsJy8B6wKGg2Jsopct-0dYNhKJGf9RdnrnTqBOt4kxy6LzxMw@mail.gmail.com","subject":"[RFC] Introduce two new commands, switch-branch and restore-paths","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-20T17:45:54Z","receivedAt":"2018-11-20T17:46:02Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Nov 19, 2018 at 04:19:53PM +0100, Duy Nguyen wrote:\n> I promise to come back with something better (at least it still\n> sounds better in my mind). If that idea does not work out, we can\n> come back and see if we can improve this.\n\nSo this is it. The patch isn't pretty, mostly as a proof of\nconcept. Just look at the three functions at the bottom of checkout.c,\nwhich is the main thing.\n\nThis patch tries to split \"git checkout\" command in two new ones:\n\n- git switch-branch is all about switching branches\n- git restore-paths (maybe restore-file is better) for checking out\n  paths\n\nThe main idea is these two commands will co-exist with the good old\n'git checkout', which will NOT be deprecated. Old timers will still\nuse \"git checkout\". But new people should be introduced to the new two\ninstead. And the new ones are just as capable as \"git checkout\".\n\nSince the three commands will co-exist (with duplicate functionality),\nmaintenance cost must be kept to minimum. The way I did this is simply\nsplit the command line options into three pieces: common,\nswitch-branch and checkout-paths. \"git checkout\" has all three, the\nother two have common and another piece.\n\nWith this, a new option added to git checkout will be automatically\navailable in either switch-branch or checkout-paths. Bug fixes apply\nto all relevant commands.\n\nLater on, we could start to add a bit more stuff in, e.g. some form of\ndisambiguation is no longer needed when running as switch-branch, or\nrestore-paths.\n\nSo, what do you think?\n\n-- 8< --\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..6e321ec8a4 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -214,6 +214,7 @@ extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);\n extern int cmd_repack(int argc, const char **argv, const char *prefix);\n extern int cmd_rerere(int argc, const char **argv, const char *prefix);\n extern int cmd_reset(int argc, const char **argv, const char *prefix);\n+extern int cmd_restore_paths(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);\n extern int cmd_revert(int argc, const char **argv, const char *prefix);\n@@ -227,6 +228,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n+extern int cmd_switch_branch(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_tag(int argc, const char **argv, const char *prefix);\n extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex acdafc6e4c..868ca3c223 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -33,6 +33,16 @@ static const char * const checkout_usage[] = {\n \tNULL,\n };\n \n+static const char * const switch_branch_usage[] = {\n+\tN_(\"git switch-branch [<options>] <branch>\"),\n+\tNULL,\n+};\n+\n+static const char * const restore_paths_usage[] = {\n+\tN_(\"git restore-paths [<options>] [<branch>] -- <file>...\"),\n+\tNULL,\n+};\n+\n struct checkout_opts {\n \tint patch_mode;\n \tint quiet;\n@@ -44,6 +54,7 @@ struct checkout_opts {\n \tint ignore_skipworktree;\n \tint ignore_other_worktrees;\n \tint show_progress;\n+\tint dwim_new_local_branch;\n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n \t * should be updated accordingly.\n@@ -55,6 +66,7 @@ struct checkout_opts {\n \tint new_branch_log;\n \tenum branch_track track;\n \tstruct diff_options diff_options;\n+\tchar *conflict_style;\n \n \tint branch_exists;\n \tconst char *prefix;\n@@ -1223,78 +1235,105 @@ static int checkout_branch(struct checkout_opts *opts,\n \treturn switch_branches(opts, new_branch_info);\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static struct option *add_common_options(struct checkout_opts *opts,\n+\t\t\t\t\t struct option *prevopts)\n {\n-\tstruct checkout_opts opts;\n-\tstruct branch_info new_branch_info;\n-\tchar *conflict_style = NULL;\n-\tint dwim_new_local_branch = 1;\n-\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n-\t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n-\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n+\t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n+\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n+\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n+\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n+\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_switch_branch_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n-\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n-\t\tOPT_BOOL(0, \"detach\", &opts.force_detach, N_(\"detach HEAD at named commit\")),\n-\t\tOPT_SET_INT('t', \"track\",  &opts.track, N_(\"set upstream info for new branch\"),\n+\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n-\t\tOPT_STRING(0, \"orphan\", &opts.new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-\t\tOPT_SET_INT_F('2', \"ours\", &opts.writeout_stage,\n+\t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_HIDDEN_BOOL(0, \"guess\", &opts->dwim_new_local_branch,\n+\t\t\t\tN_(\"second guess 'git checkout <no-such-branch>'\")),\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n+\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_checkout_path_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n-\t\tOPT_SET_INT_F('3', \"theirs\", &opts.writeout_stage,\n+\t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts.force, N_(\"force checkout (throw away local modifications)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts.merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts.overwrite_ignore,\n-\t\t\t   N_(\"update ignored files (default)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n-\t\tOPT_BOOL('p', \"patch\", &opts.patch_mode, N_(\"select hunks interactively\")),\n-\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts.ignore_skipworktree,\n-\t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_HIDDEN_BOOL(0, \"guess\", &dwim_new_local_branch,\n-\t\t\t\tN_(\"second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n-\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n-\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n-\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n-\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress reporting\")),\n-\t\tOPT_END(),\n+\t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_END()\n \t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n \n-\tmemset(&opts, 0, sizeof(opts));\n+static int checkout_main(int argc, const char **argv, const char *prefix,\n+\t\t\t struct checkout_opts *opts, struct option *options,\n+\t\t\t const char * const usagestr[])\n+{\n+\tstruct branch_info new_branch_info;\n+\tint dwim_remotes_matched = 0;\n+\n+\tmemset(opts, 0, sizeof(*opts));\n+\topts->dwim_new_local_branch = 1;\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n-\topts.overwrite_ignore = 1;\n-\topts.prefix = prefix;\n-\topts.show_progress = -1;\n+\topts->overwrite_ignore = 1;\n+\topts->prefix = prefix;\n+\topts->show_progress = -1;\n \n-\tgit_config(git_checkout_config, &opts);\n+\tgit_config(git_checkout_config, opts);\n \n-\topts.track = BRANCH_TRACK_UNSPECIFIED;\n+\topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\targc = parse_options(argc, argv, prefix, options, checkout_usage,\n+\targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tif (opts.show_progress < 0) {\n-\t\tif (opts.quiet)\n-\t\t\topts.show_progress = 0;\n+\tif (opts->show_progress < 0) {\n+\t\tif (opts->quiet)\n+\t\t\topts->show_progress = 0;\n \t\telse\n-\t\t\topts.show_progress = isatty(2);\n+\t\t\topts->show_progress = isatty(2);\n \t}\n \n-\tif (conflict_style) {\n-\t\topts.merge = 1; /* implied */\n-\t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n+\tif (opts->conflict_style) {\n+\t\topts->merge = 1; /* implied */\n+\t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n \n-\tif ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)\n+\tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n \n \t/*\n@@ -1302,14 +1341,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t * and new_branch_force and new_orphan_branch will tell us which one of\n \t * -b/-B/--orphan is being used.\n \t */\n-\tif (opts.new_branch_force)\n-\t\topts.new_branch = opts.new_branch_force;\n+\tif (opts->new_branch_force)\n+\t\topts->new_branch = opts->new_branch_force;\n \n-\tif (opts.new_orphan_branch)\n-\t\topts.new_branch = opts.new_orphan_branch;\n+\tif (opts->new_orphan_branch)\n+\t\topts->new_branch = opts->new_orphan_branch;\n \n \t/* --track without -b/-B/--orphan should DWIM */\n-\tif (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {\n+\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n \t\tconst char *argv0 = argv[0];\n \t\tif (!argc || !strcmp(argv0, \"--\"))\n \t\t\tdie(_(\"--track needs a branch name\"));\n@@ -1318,7 +1357,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\targv0 = strchr(argv0, '/');\n \t\tif (!argv0 || !argv0[1])\n \t\t\tdie(_(\"missing branch name; try -b\"));\n-\t\topts.new_branch = argv0 + 1;\n+\t\topts->new_branch = argv0 + 1;\n \t}\n \n \t/*\n@@ -1337,56 +1376,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tif (argc) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n-\t\t\t!opts.patch_mode &&\n-\t\t\tdwim_new_local_branch &&\n-\t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n-\t\t\t!opts.new_branch;\n+\t\t\t!opts->patch_mode &&\n+\t\t\topts->dwim_new_local_branch &&\n+\t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n+\t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &new_branch_info, opts, &rev,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\n \n \tif (argc) {\n-\t\tparse_pathspec(&opts.pathspec, 0,\n-\t\t\t       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n+\t\tparse_pathspec(&opts->pathspec, 0,\n+\t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n \t\t\t       prefix, argv);\n \n-\t\tif (!opts.pathspec.nr)\n+\t\tif (!opts->pathspec.nr)\n \t\t\tdie(_(\"invalid path specification\"));\n \n \t\t/*\n \t\t * Try to give more helpful suggestion.\n \t\t * new_branch && argc > 1 will be caught later.\n \t\t */\n-\t\tif (opts.new_branch && argc == 1)\n+\t\tif (opts->new_branch && argc == 1)\n \t\t\tdie(_(\"'%s' is not a commit and a branch '%s' cannot be created from it\"),\n-\t\t\t\targv[0], opts.new_branch);\n+\t\t\t\targv[0], opts->new_branch);\n \n-\t\tif (opts.force_detach)\n+\t\tif (opts->force_detach)\n \t\t\tdie(_(\"git checkout: --detach does not take a path argument '%s'\"),\n \t\t\t    argv[0]);\n \n-\t\tif (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)\n+\t\tif (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)\n \t\t\tdie(_(\"git checkout: --ours/--theirs, --force and --merge are incompatible when\\n\"\n \t\t\t      \"checking out of the index.\"));\n \t}\n \n-\tif (opts.new_branch) {\n+\tif (opts->new_branch) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tif (opts.new_branch_force)\n-\t\t\topts.branch_exists = validate_branchname(opts.new_branch, &buf);\n+\t\tif (opts->new_branch_force)\n+\t\t\topts->branch_exists = validate_branchname(opts->new_branch, &buf);\n \t\telse\n-\t\t\topts.branch_exists =\n-\t\t\t\tvalidate_new_branchname(opts.new_branch, &buf, 0);\n+\t\t\topts->branch_exists =\n+\t\t\t\tvalidate_new_branchname(opts->new_branch, &buf, 0);\n \t\tstrbuf_release(&buf);\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr) {\n-\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\tif (opts->patch_mode || opts->pathspec.nr) {\n+\t\tint ret = checkout_paths(opts, new_branch_info.name);\n \t\tif (ret && dwim_remotes_matched > 1 &&\n \t\t    advice_checkout_ambiguous_remote_branch_name)\n \t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n@@ -1405,6 +1444,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n-\t\treturn checkout_branch(&opts, &new_branch_info);\n+\t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, checkout_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\n+\n+int cmd_switch_branch(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, switch_branch_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\n+\n+int cmd_restore_paths(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, restore_paths_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\ndiff --git a/git.c b/git.c\nindex 2f604a41ea..e8a76a99da 100644\n--- a/git.c\n+++ b/git.c\n@@ -542,6 +542,7 @@ static struct cmd_struct commands[] = {\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n+\t{ \"restore-paths\", cmd_restore_paths, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"rev-list\", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },\n \t{ \"rev-parse\", cmd_rev_parse, NO_PARSEOPT },\n \t{ \"revert\", cmd_revert, RUN_SETUP | NEED_WORK_TREE },\n@@ -557,6 +558,7 @@ static struct cmd_struct commands[] = {\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n+\t{ \"switch-branch\", cmd_switch_branch, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n \t{ \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n \t{ \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex 8c9edce52f..c609d52926 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -126,7 +126,7 @@ struct option *parse_options_concat(struct option *a, struct option *b)\n \tstruct option *ret;\n \tsize_t i, a_len = 0, b_len = 0;\n \n-\tfor (i = 0; a[i].type != OPTION_END; i++)\n+\tfor (i = 0; a && a[i].type != OPTION_END; i++)\n \t\ta_len++;\n \tfor (i = 0; b[i].type != OPTION_END; i++)\n \t\tb_len++;\n-- 8< --\n"},{"id":"364042","messageId":"20181125222021.GL4883@hank.intra.tgummerer.com","threadId":"49793","inReplyTo":"20181120174554.GA29910@duynguyen.home","subject":"Re: [RFC] Introduce two new commands, switch-branch and restore-paths","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2018-11-25T22:20:21Z","receivedAt":"2018-11-25T22:20:54Z","isPatch":false,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 11/20, Duy Nguyen wrote:\n> On Mon, Nov 19, 2018 at 04:19:53PM +0100, Duy Nguyen wrote:\n> > I promise to come back with something better (at least it still\n> > sounds better in my mind). If that idea does not work out, we can\n> > come back and see if we can improve this.\n> \n> So this is it. The patch isn't pretty, mostly as a proof of\n> concept. Just look at the three functions at the bottom of checkout.c,\n> which is the main thing.\n> \n> This patch tries to split \"git checkout\" command in two new ones:\n> \n> - git switch-branch is all about switching branches\n> - git restore-paths (maybe restore-file is better) for checking out\n>   paths\n> \n> The main idea is these two commands will co-exist with the good old\n> 'git checkout', which will NOT be deprecated. Old timers will still\n> use \"git checkout\". But new people should be introduced to the new two\n> instead. And the new ones are just as capable as \"git checkout\".\n> \n> Since the three commands will co-exist (with duplicate functionality),\n> maintenance cost must be kept to minimum. The way I did this is simply\n> split the command line options into three pieces: common,\n> switch-branch and checkout-paths. \"git checkout\" has all three, the\n> other two have common and another piece.\n>\n> With this, a new option added to git checkout will be automatically\n> available in either switch-branch or checkout-paths. Bug fixes apply\n> to all relevant commands.\n> \n> Later on, we could start to add a bit more stuff in, e.g. some form of\n> disambiguation is no longer needed when running as switch-branch, or\n> restore-paths.\n> \n> So, what do you think?\n\nI like the idea of splitting those commands up, in fact it is\nsomething I've been considering working on myself.  I do think we\nshould consider if we want to change the behaviour of those new\ncommands in any way compared to 'git checkout', since we're starting\nwith a clean slate.\n\nOne thing in particular that I have in mind is something I'm currently\nworking on, namely adding a --index flag to 'git checkout', which\nwould make 'git checkout' work in non-overlay mode (for more\ndiscussion on that see also [*1*].  I got something working, that\nneeds to be polished a bit and am hoping to send that to the list\nsometime soon.\n\nI wonder if such the --index behaviour could be the default in\nrestore-paths command?\n\nMost of the underlying machinery for 'checkout' could and should of\ncourse still be shared between the commands.\n\n*1*: <xmqq4loqplou.fsf@gitster.mtv.corp.google.com>\n\n> -- 8< --\n> diff --git a/builtin.h b/builtin.h\n> index 6538932e99..6e321ec8a4 100644\n> --- a/builtin.h\n> +++ b/builtin.h\n> @@ -214,6 +214,7 @@ extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);\n>  extern int cmd_repack(int argc, const char **argv, const char *prefix);\n>  extern int cmd_rerere(int argc, const char **argv, const char *prefix);\n>  extern int cmd_reset(int argc, const char **argv, const char *prefix);\n> +extern int cmd_restore_paths(int argc, const char **argv, const char *prefix);\n>  extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\n>  extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);\n>  extern int cmd_revert(int argc, const char **argv, const char *prefix);\n> @@ -227,6 +228,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n>  extern int cmd_status(int argc, const char **argv, const char *prefix);\n>  extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n>  extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n> +extern int cmd_switch_branch(int argc, const char **argv, const char *prefix);\n>  extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n>  extern int cmd_tag(int argc, const char **argv, const char *prefix);\n>  extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index acdafc6e4c..868ca3c223 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -33,6 +33,16 @@ static const char * const checkout_usage[] = {\n>  \tNULL,\n>  };\n>  \n> +static const char * const switch_branch_usage[] = {\n> +\tN_(\"git switch-branch [<options>] <branch>\"),\n> +\tNULL,\n> +};\n> +\n> +static const char * const restore_paths_usage[] = {\n> +\tN_(\"git restore-paths [<options>] [<branch>] -- <file>...\"),\n> +\tNULL,\n> +};\n> +\n>  struct checkout_opts {\n>  \tint patch_mode;\n>  \tint quiet;\n> @@ -44,6 +54,7 @@ struct checkout_opts {\n>  \tint ignore_skipworktree;\n>  \tint ignore_other_worktrees;\n>  \tint show_progress;\n> +\tint dwim_new_local_branch;\n>  \t/*\n>  \t * If new checkout options are added, skip_merge_working_tree\n>  \t * should be updated accordingly.\n> @@ -55,6 +66,7 @@ struct checkout_opts {\n>  \tint new_branch_log;\n>  \tenum branch_track track;\n>  \tstruct diff_options diff_options;\n> +\tchar *conflict_style;\n>  \n>  \tint branch_exists;\n>  \tconst char *prefix;\n> @@ -1223,78 +1235,105 @@ static int checkout_branch(struct checkout_opts *opts,\n>  \treturn switch_branches(opts, new_branch_info);\n>  }\n>  \n> -int cmd_checkout(int argc, const char **argv, const char *prefix)\n> +static struct option *add_common_options(struct checkout_opts *opts,\n> +\t\t\t\t\t struct option *prevopts)\n>  {\n> -\tstruct checkout_opts opts;\n> -\tstruct branch_info new_branch_info;\n> -\tchar *conflict_style = NULL;\n> -\tint dwim_new_local_branch = 1;\n> -\tint dwim_remotes_matched = 0;\n>  \tstruct option options[] = {\n> -\t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n> -\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n> +\t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n> +\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n> +\t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n> +\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n> +\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n> +\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n> +\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n> +\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n> +\t\t\t   PARSE_OPT_NOCOMPLETE),\n> +\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n> +\t\t\t   N_(\"conflict style (merge or diff3)\")),\n> +\t\tOPT_END()\n> +\t};\n> +\tstruct option *newopts = parse_options_concat(prevopts, options);\n> +\tfree(prevopts);\n> +\treturn newopts;\n> +}\n> +\n> +static struct option *add_switch_branch_options(struct checkout_opts *opts,\n> +\t\t\t\t\t\tstruct option *prevopts)\n> +{\n> +\tstruct option options[] = {\n> +\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n>  \t\t\t   N_(\"create and checkout a new branch\")),\n> -\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n> +\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n>  \t\t\t   N_(\"create/reset and checkout a branch\")),\n> -\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n> -\t\tOPT_BOOL(0, \"detach\", &opts.force_detach, N_(\"detach HEAD at named commit\")),\n> -\t\tOPT_SET_INT('t', \"track\",  &opts.track, N_(\"set upstream info for new branch\"),\n> +\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n> +\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n> +\t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n>  \t\t\tBRANCH_TRACK_EXPLICIT),\n> -\t\tOPT_STRING(0, \"orphan\", &opts.new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n> -\t\tOPT_SET_INT_F('2', \"ours\", &opts.writeout_stage,\n> +\t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n> +\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n> +\t\tOPT_HIDDEN_BOOL(0, \"guess\", &opts->dwim_new_local_branch,\n> +\t\t\t\tN_(\"second guess 'git checkout <no-such-branch>'\")),\n> +\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n> +\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n> +\t\tOPT_END()\n> +\t};\n> +\tstruct option *newopts = parse_options_concat(prevopts, options);\n> +\tfree(prevopts);\n> +\treturn newopts;\n> +}\n> +\n> +static struct option *add_checkout_path_options(struct checkout_opts *opts,\n> +\t\t\t\t\t\tstruct option *prevopts)\n> +{\n> +\tstruct option options[] = {\n> +\t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n>  \t\t\t      N_(\"checkout our version for unmerged files\"),\n>  \t\t\t      2, PARSE_OPT_NONEG),\n> -\t\tOPT_SET_INT_F('3', \"theirs\", &opts.writeout_stage,\n> +\t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n>  \t\t\t      N_(\"checkout their version for unmerged files\"),\n>  \t\t\t      3, PARSE_OPT_NONEG),\n> -\t\tOPT__FORCE(&opts.force, N_(\"force checkout (throw away local modifications)\"),\n> -\t\t\t   PARSE_OPT_NOCOMPLETE),\n> -\t\tOPT_BOOL('m', \"merge\", &opts.merge, N_(\"perform a 3-way merge with the new branch\")),\n> -\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts.overwrite_ignore,\n> -\t\t\t   N_(\"update ignored files (default)\"),\n> -\t\t\t   PARSE_OPT_NOCOMPLETE),\n> -\t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n> -\t\t\t   N_(\"conflict style (merge or diff3)\")),\n> -\t\tOPT_BOOL('p', \"patch\", &opts.patch_mode, N_(\"select hunks interactively\")),\n> -\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts.ignore_skipworktree,\n> -\t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n> -\t\tOPT_HIDDEN_BOOL(0, \"guess\", &dwim_new_local_branch,\n> -\t\t\t\tN_(\"second guess 'git checkout <no-such-branch>'\")),\n> -\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n> -\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n> -\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n> -\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n> -\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n> -\t\tOPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress reporting\")),\n> -\t\tOPT_END(),\n> +\t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n> +\t\tOPT_END()\n>  \t};\n> +\tstruct option *newopts = parse_options_concat(prevopts, options);\n> +\tfree(prevopts);\n> +\treturn newopts;\n> +}\n>  \n> -\tmemset(&opts, 0, sizeof(opts));\n> +static int checkout_main(int argc, const char **argv, const char *prefix,\n> +\t\t\t struct checkout_opts *opts, struct option *options,\n> +\t\t\t const char * const usagestr[])\n> +{\n> +\tstruct branch_info new_branch_info;\n> +\tint dwim_remotes_matched = 0;\n> +\n> +\tmemset(opts, 0, sizeof(*opts));\n> +\topts->dwim_new_local_branch = 1;\n>  \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n> -\topts.overwrite_ignore = 1;\n> -\topts.prefix = prefix;\n> -\topts.show_progress = -1;\n> +\topts->overwrite_ignore = 1;\n> +\topts->prefix = prefix;\n> +\topts->show_progress = -1;\n>  \n> -\tgit_config(git_checkout_config, &opts);\n> +\tgit_config(git_checkout_config, opts);\n>  \n> -\topts.track = BRANCH_TRACK_UNSPECIFIED;\n> +\topts->track = BRANCH_TRACK_UNSPECIFIED;\n>  \n> -\targc = parse_options(argc, argv, prefix, options, checkout_usage,\n> +\targc = parse_options(argc, argv, prefix, options, usagestr,\n>  \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n>  \n> -\tif (opts.show_progress < 0) {\n> -\t\tif (opts.quiet)\n> -\t\t\topts.show_progress = 0;\n> +\tif (opts->show_progress < 0) {\n> +\t\tif (opts->quiet)\n> +\t\t\topts->show_progress = 0;\n>  \t\telse\n> -\t\t\topts.show_progress = isatty(2);\n> +\t\t\topts->show_progress = isatty(2);\n>  \t}\n>  \n> -\tif (conflict_style) {\n> -\t\topts.merge = 1; /* implied */\n> -\t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n> +\tif (opts->conflict_style) {\n> +\t\topts->merge = 1; /* implied */\n> +\t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n>  \t}\n>  \n> -\tif ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)\n> +\tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n>  \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n>  \n>  \t/*\n> @@ -1302,14 +1341,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \t * and new_branch_force and new_orphan_branch will tell us which one of\n>  \t * -b/-B/--orphan is being used.\n>  \t */\n> -\tif (opts.new_branch_force)\n> -\t\topts.new_branch = opts.new_branch_force;\n> +\tif (opts->new_branch_force)\n> +\t\topts->new_branch = opts->new_branch_force;\n>  \n> -\tif (opts.new_orphan_branch)\n> -\t\topts.new_branch = opts.new_orphan_branch;\n> +\tif (opts->new_orphan_branch)\n> +\t\topts->new_branch = opts->new_orphan_branch;\n>  \n>  \t/* --track without -b/-B/--orphan should DWIM */\n> -\tif (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {\n> +\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n>  \t\tconst char *argv0 = argv[0];\n>  \t\tif (!argc || !strcmp(argv0, \"--\"))\n>  \t\t\tdie(_(\"--track needs a branch name\"));\n> @@ -1318,7 +1357,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \t\targv0 = strchr(argv0, '/');\n>  \t\tif (!argv0 || !argv0[1])\n>  \t\t\tdie(_(\"missing branch name; try -b\"));\n> -\t\topts.new_branch = argv0 + 1;\n> +\t\topts->new_branch = argv0 + 1;\n>  \t}\n>  \n>  \t/*\n> @@ -1337,56 +1376,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \tif (argc) {\n>  \t\tstruct object_id rev;\n>  \t\tint dwim_ok =\n> -\t\t\t!opts.patch_mode &&\n> -\t\t\tdwim_new_local_branch &&\n> -\t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n> -\t\t\t!opts.new_branch;\n> +\t\t\t!opts->patch_mode &&\n> +\t\t\topts->dwim_new_local_branch &&\n> +\t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n> +\t\t\t!opts->new_branch;\n>  \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n> -\t\t\t\t\t     &new_branch_info, &opts, &rev,\n> +\t\t\t\t\t     &new_branch_info, opts, &rev,\n>  \t\t\t\t\t     &dwim_remotes_matched);\n>  \t\targv += n;\n>  \t\targc -= n;\n>  \t}\n>  \n>  \tif (argc) {\n> -\t\tparse_pathspec(&opts.pathspec, 0,\n> -\t\t\t       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n> +\t\tparse_pathspec(&opts->pathspec, 0,\n> +\t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n>  \t\t\t       prefix, argv);\n>  \n> -\t\tif (!opts.pathspec.nr)\n> +\t\tif (!opts->pathspec.nr)\n>  \t\t\tdie(_(\"invalid path specification\"));\n>  \n>  \t\t/*\n>  \t\t * Try to give more helpful suggestion.\n>  \t\t * new_branch && argc > 1 will be caught later.\n>  \t\t */\n> -\t\tif (opts.new_branch && argc == 1)\n> +\t\tif (opts->new_branch && argc == 1)\n>  \t\t\tdie(_(\"'%s' is not a commit and a branch '%s' cannot be created from it\"),\n> -\t\t\t\targv[0], opts.new_branch);\n> +\t\t\t\targv[0], opts->new_branch);\n>  \n> -\t\tif (opts.force_detach)\n> +\t\tif (opts->force_detach)\n>  \t\t\tdie(_(\"git checkout: --detach does not take a path argument '%s'\"),\n>  \t\t\t    argv[0]);\n>  \n> -\t\tif (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)\n> +\t\tif (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)\n>  \t\t\tdie(_(\"git checkout: --ours/--theirs, --force and --merge are incompatible when\\n\"\n>  \t\t\t      \"checking out of the index.\"));\n>  \t}\n>  \n> -\tif (opts.new_branch) {\n> +\tif (opts->new_branch) {\n>  \t\tstruct strbuf buf = STRBUF_INIT;\n>  \n> -\t\tif (opts.new_branch_force)\n> -\t\t\topts.branch_exists = validate_branchname(opts.new_branch, &buf);\n> +\t\tif (opts->new_branch_force)\n> +\t\t\topts->branch_exists = validate_branchname(opts->new_branch, &buf);\n>  \t\telse\n> -\t\t\topts.branch_exists =\n> -\t\t\t\tvalidate_new_branchname(opts.new_branch, &buf, 0);\n> +\t\t\topts->branch_exists =\n> +\t\t\t\tvalidate_new_branchname(opts->new_branch, &buf, 0);\n>  \t\tstrbuf_release(&buf);\n>  \t}\n>  \n>  \tUNLEAK(opts);\n> -\tif (opts.patch_mode || opts.pathspec.nr) {\n> -\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n> +\tif (opts->patch_mode || opts->pathspec.nr) {\n> +\t\tint ret = checkout_paths(opts, new_branch_info.name);\n>  \t\tif (ret && dwim_remotes_matched > 1 &&\n>  \t\t    advice_checkout_ambiguous_remote_branch_name)\n>  \t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n> @@ -1405,6 +1444,49 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \t\t\t       dwim_remotes_matched);\n>  \t\treturn ret;\n>  \t} else {\n> -\t\treturn checkout_branch(&opts, &new_branch_info);\n> +\t\treturn checkout_branch(opts, &new_branch_info);\n>  \t}\n>  }\n> +\n> +int cmd_checkout(int argc, const char **argv, const char *prefix)\n> +{\n> +\tstruct checkout_opts opts;\n> +\tstruct option *options = NULL;\n> +\tint ret;\n> +\n> +\toptions = add_common_options(&opts, options);\n> +\toptions = add_switch_branch_options(&opts, options);\n> +\toptions = add_checkout_path_options(&opts, options);\n> +\tret = checkout_main(argc, argv, prefix, &opts,\n> +\t\t\t    options, checkout_usage);\n> +\tFREE_AND_NULL(options);\n> +\treturn ret;\n> +}\n> +\n> +int cmd_switch_branch(int argc, const char **argv, const char *prefix)\n> +{\n> +\tstruct checkout_opts opts;\n> +\tstruct option *options = NULL;\n> +\tint ret;\n> +\n> +\toptions = add_common_options(&opts, options);\n> +\toptions = add_switch_branch_options(&opts, options);\n> +\tret = checkout_main(argc, argv, prefix, &opts,\n> +\t\t\t    options, switch_branch_usage);\n> +\tFREE_AND_NULL(options);\n> +\treturn ret;\n> +}\n> +\n> +int cmd_restore_paths(int argc, const char **argv, const char *prefix)\n> +{\n> +\tstruct checkout_opts opts;\n> +\tstruct option *options = NULL;\n> +\tint ret;\n> +\n> +\toptions = add_common_options(&opts, options);\n> +\toptions = add_checkout_path_options(&opts, options);\n> +\tret = checkout_main(argc, argv, prefix, &opts,\n> +\t\t\t    options, restore_paths_usage);\n> +\tFREE_AND_NULL(options);\n> +\treturn ret;\n> +}\n> diff --git a/git.c b/git.c\n> index 2f604a41ea..e8a76a99da 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -542,6 +542,7 @@ static struct cmd_struct commands[] = {\n>  \t{ \"replace\", cmd_replace, RUN_SETUP },\n>  \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n>  \t{ \"reset\", cmd_reset, RUN_SETUP },\n> +\t{ \"restore-paths\", cmd_restore_paths, RUN_SETUP | NEED_WORK_TREE },\n>  \t{ \"rev-list\", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },\n>  \t{ \"rev-parse\", cmd_rev_parse, NO_PARSEOPT },\n>  \t{ \"revert\", cmd_revert, RUN_SETUP | NEED_WORK_TREE },\n> @@ -557,6 +558,7 @@ static struct cmd_struct commands[] = {\n>  \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n>  \t{ \"stripspace\", cmd_stripspace },\n>  \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n> +\t{ \"switch-branch\", cmd_switch_branch, RUN_SETUP | NEED_WORK_TREE },\n>  \t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n>  \t{ \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n>  \t{ \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\n> diff --git a/parse-options-cb.c b/parse-options-cb.c\n> index 8c9edce52f..c609d52926 100644\n> --- a/parse-options-cb.c\n> +++ b/parse-options-cb.c\n> @@ -126,7 +126,7 @@ struct option *parse_options_concat(struct option *a, struct option *b)\n>  \tstruct option *ret;\n>  \tsize_t i, a_len = 0, b_len = 0;\n>  \n> -\tfor (i = 0; a[i].type != OPTION_END; i++)\n> +\tfor (i = 0; a && a[i].type != OPTION_END; i++)\n>  \t\ta_len++;\n>  \tfor (i = 0; b[i].type != OPTION_END; i++)\n>  \t\tb_len++;\n> -- 8< --\n"},{"id":"364048","messageId":"xmqq1s781qx8.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"20181125222021.GL4883@hank.intra.tgummerer.com","subject":"Re: [RFC] Introduce two new commands, switch-branch and restore-paths","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-26T03:03:15Z","receivedAt":"2018-11-26T03:03:20Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n> I like the idea of splitting those commands up, in fact it is\n> something I've been considering working on myself.  I do think we\n> should consider if we want to change the behaviour of those new\n> commands in any way compared to 'git checkout', since we're starting\n> with a clean slate.\n>\n> One thing in particular that I have in mind is something I'm currently\n> working on, namely adding a --index flag to 'git checkout', which\n> would make 'git checkout' work in non-overlay mode (for more\n> discussion on that see also [*1*].\n\nAh, thanks for reminding me of that.  That explains why I felt\nuneasy to see \"restore\" in the proposed command name.  In short, I\nthink \"checkout --index <tree> <pathspec>\", i.e. if the <pathspec>\nmatches a directory in <tree> and the current index and/or the\nworking tree has tracked paths in that directory that do not exist\nin <tree>, the operation _removes_ these paths so that the result\nmatches <tree>, should become the default of \"I want to check out\nthese paths from the named tree-ish\".  The current one is not\nexactly \"checking out the paths\" in that it ignores and does not\ncheck out the absense of paths in <tree>.  That operation sounds\nmore like \"restoring paths out of a given tree\".  If the tree does\nnot have some paths, these paths won't be \"restored\" from that tree,\nso \"restore\" matches the current \"overlay what's taken out of the\ngiven tree on top of what is already in the index and the working\ntree, without checking out the absense of paths\" better from that\npoint of view.\n\n\n"},{"id":"364075","messageId":"CACsJy8Dn2xWWkivxT1VYQak8sHwvFEHrz5VALeajfTs2feuNtw@mail.gmail.com","threadId":"49793","inReplyTo":"xmqq1s781qx8.fsf@gitster-ct.c.googlers.com","subject":"Re: [RFC] Introduce two new commands, switch-branch and restore-paths","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-26T15:37:41Z","receivedAt":"2018-11-26T15:38:11Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Nov 26, 2018 at 4:03 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Thomas Gummerer <t.gummerer@gmail.com> writes:\n>\n> > I like the idea of splitting those commands up, in fact it is\n> > something I've been considering working on myself.  I do think we\n> > should consider if we want to change the behaviour of those new\n> > commands in any way compared to 'git checkout', since we're starting\n> > with a clean slate.\n\nBetter defaults? Hell yes!\n\n> > One thing in particular that I have in mind is something I'm currently\n> > working on, namely adding a --index flag to 'git checkout', which\n> > would make 'git checkout' work in non-overlay mode (for more\n> > discussion on that see also [*1*].\n>\n> Ah, thanks for reminding me of that.  That explains why I felt\n> uneasy to see \"restore\" in the proposed command name.\n\nAbout that name. I didn't want to start the command name with checkout\nto avoid completion conflict (the obvious choice was checkout-path,\nthe function name behind it). And I didn't find any other good name,\nso I picked \"restore\" out of git-checkout.txt's one-line description.\nIf we end up with a better command name, perhaps reword that line too.\n-- \nDuy\n"},{"id":"364081","messageId":"87o9abzv46.fsf@evledraar.gmail.com","threadId":"49793","inReplyTo":"20181120174554.GA29910@duynguyen.home","subject":"Re: [RFC] Introduce two new commands, switch-branch and restore-paths","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-11-26T16:00:57Z","receivedAt":"2018-11-26T16:01:02Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Nov 20 2018, Duy Nguyen wrote:\n\n> On Mon, Nov 19, 2018 at 04:19:53PM +0100, Duy Nguyen wrote:\n>> I promise to come back with something better (at least it still\n>> sounds better in my mind). If that idea does not work out, we can\n>> come back and see if we can improve this.\n>\n> So this is it. The patch isn't pretty, mostly as a proof of\n> concept. Just look at the three functions at the bottom of checkout.c,\n> which is the main thing.\n>\n> This patch tries to split \"git checkout\" command in two new ones:\n>\n> - git switch-branch is all about switching branches\n\nIsn't this going to also take the other ref arguments 'git checkout'\ntakes now? I.e. tags, detached HEADs etc? I'm reminded of the discussion\nabout what \"range-diff\" should be called :)\n\n> - git restore-paths (maybe restore-file is better) for checking out\n>   paths\n\nIf it takes globs/dirs then a plural is probably better.\n\n> The main idea is these two commands will co-exist with the good old\n> 'git checkout', which will NOT be deprecated. Old timers will still\n> use \"git checkout\". But new people should be introduced to the new two\n> instead. And the new ones are just as capable as \"git checkout\".\n>\n> Since the three commands will co-exist (with duplicate functionality),\n> maintenance cost must be kept to minimum. The way I did this is simply\n> split the command line options into three pieces: common,\n> switch-branch and checkout-paths. \"git checkout\" has all three, the\n> other two have common and another piece.\n>\n> With this, a new option added to git checkout will be automatically\n> available in either switch-branch or checkout-paths. Bug fixes apply\n> to all relevant commands.\n>\n> Later on, we could start to add a bit more stuff in, e.g. some form of\n> disambiguation is no longer needed when running as switch-branch, or\n> restore-paths.\n>\n> So, what do you think?\n\nThat \"git checkout\" does too many things is something that keeps coming\nup in online discussions about Git's UI. Two things:\n\na) It would really help to have some comparison of cases where these\n   split commands are much clearer or less ambiguous than\n   git-checkout. I can think of some (e.g. branch with the same name as\n   a file) but having some overall picture of what the new UI looks like\n   with solved / not solved cases would be nice. Also a comparison with\n   other SCMs people find less confusing (svn, hg, bzr, ...)\n\nb) I think we really need to have some end-game where we'd actually\n   switch away from \"checkout\" (which we could still auto-route to new\n   commands in perpetuity, but print a warning or error). Otherwise\n   we'll just end up with https://xkcd.com/927/ and more UI confusion\n   for all.\n"},{"id":"364083","messageId":"CACsJy8AU9HUPohHat7aGxFHuz9LAphKUTnMG=UoiZE-Foetddg@mail.gmail.com","threadId":"49793","inReplyTo":"87o9abzv46.fsf@evledraar.gmail.com","subject":"Re: [RFC] Introduce two new commands, switch-branch and restore-paths","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-26T16:08:30Z","receivedAt":"2018-11-26T16:09:00Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Nov 26, 2018 at 5:01 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> > So, what do you think?\n>\n> That \"git checkout\" does too many things is something that keeps coming\n> up in online discussions about Git's UI. Two things:\n>\n> a) It would really help to have some comparison of cases where these\n>    split commands are much clearer or less ambiguous than\n>    git-checkout. I can think of some (e.g. branch with the same name as\n>    a file) but having some overall picture of what the new UI looks like\n>    with solved / not solved cases would be nice. Also a comparison with\n>    other SCMs people find less confusing (svn, hg, bzr, ...)\n\nLess ambiguous is indeed one of the reasons I wanted to do this.\n\n> b) I think we really need to have some end-game where we'd actually\n>    switch away from \"checkout\" (which we could still auto-route to new\n>    commands in perpetuity, but print a warning or error). Otherwise\n>    we'll just end up with https://xkcd.com/927/ and more UI confusion\n>    for all.\n\nI'm not going to remove \"git checkout\". Not until the majority of\nusers are really in favor of the new ones. So my end-game plan is to\njust promote the two new commands in man pages, tutorial, advice...\nPerhaps at some point \"git checkout\" is also removed from \"git help\".\nBut that's about it. If you want to stick with \"git checkout\", it's\nthere (and not nagging you to move to new ones).\n-- \nDuy\n"},{"id":"364104","messageId":"CAGZ79kaCidJ1s2vXaQX9b_o7Tk4O+WTdmBSM3RBKX3bCBMSFKA@mail.gmail.com","threadId":"49793","inReplyTo":"87o9abzv46.fsf@evledraar.gmail.com","subject":"Re: [RFC] Introduce two new commands, switch-branch and restore-paths","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-11-26T23:10:00Z","receivedAt":"2018-11-26T23:10:16Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, Nov 26, 2018 at 8:01 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n>\n> On Tue, Nov 20 2018, Duy Nguyen wrote:\n>\n> > On Mon, Nov 19, 2018 at 04:19:53PM +0100, Duy Nguyen wrote:\n> >> I promise to come back with something better (at least it still\n> >> sounds better in my mind). If that idea does not work out, we can\n> >> come back and see if we can improve this.\n> >\n> > So this is it. The patch isn't pretty, mostly as a proof of\n> > concept. Just look at the three functions at the bottom of checkout.c,\n> > which is the main thing.\n> >\n> > This patch tries to split \"git checkout\" command in two new ones:\n> >\n> > - git switch-branch is all about switching branches\n>\n> Isn't this going to also take the other ref arguments 'git checkout'\n> takes now? I.e. tags, detached HEADs etc? I'm reminded of the discussion\n> about what \"range-diff\" should be called :)\n\nHeh, good call. :-)\nNote that the color of a bikeshed has fewer functional implications\nthan coming up with proper names in your API exposed to millions\nof users, as cognitive associations playing mind tricks, can have a\nhuge impact on their productivity. ;-)\n\nIn a neighboring thread there is discussion of the concept of a\n'change' (and evolving the change locally), which is yet another\nthing in the refs-space.\n\n'switch-branch' sounds like a good name for a beginner who is just\ngetting started, but as soon as they discover that there is more than\nbranches (detached HEAD via commits, tags,\nremote tracking \"branches\"), this name may be confusing.\nSo it would not be a good choice for the intermediate Git user.\nThe old time power user would not care as they have 'checkout'\nin their muscle memory, aliased to 'co', but maybe they'd find\nit nice for explaining to new users.\n\nSo I'd be thrilled to find a name that serves users on all levels.\n\nMaybe we need to step back and consider what the command does.\nAnd from that I would name it \"rewire-HEAD-and-update-index&worktree\"\nand then simplify from there. For the beginner user, the concept of\nHEAD might be overwhelming, such that we don't want to have that\nin there.\n\nSo I'd be tempted to suggest to call it \"switch-to-ref\", but that would\nbe wrong in the corner case as well: When using that with a remote\ntracking branch, you don't \"switch to it\" by putting it into your HEAD,\nbut you merely checkout the commit that it's pointing at.\n\n\n\n>\n> > - git restore-paths (maybe restore-file is better) for checking out\n> >   paths\n\n\"content-to-path\", maybe(?) as it moves the content (as given by commit\nor implicitly assuming the index when omitted) into that path(, again).\n(I am not enthused about this, as you can similarly argue for\ncontent-to-paths, content-to-worktree, which then could split up into\n\"index-to-worktree [pathspec]\" as well as \"tree-to-worktree <commit>\".\nalso the notion of X-to-Y seems a novel concept in our naming, so maybe\nverb-noun is better, hence restore-path or \"fix-paths\" may be better)\n\n> > Later on, we could start to add a bit more stuff in, e.g. some form of\n> > disambiguation is no longer needed when running as switch-branch, or\n> > restore-paths.\n> >\n> > So, what do you think?\n\nThe patch looks interestingly small :-)\n\n> That \"git checkout\" does too many things is something that keeps coming\n> up in online discussions about Git's UI. Two things:\n>\n> a) It would really help to have some comparison of cases where these\n>    split commands are much clearer or less ambiguous than\n>    git-checkout. I can think of some (e.g. branch with the same name as\n>    a file) but having some overall picture of what the new UI looks like\n>    with solved / not solved cases would be nice. Also a comparison with\n>    other SCMs people find less confusing (svn, hg, bzr, ...)\n\nHow do other SCMs solve this issue? (What is their design space?\nHow many commands do they have for what git-checkout does\nall-in-one?)\n\n> b) I think we really need to have some end-game where we'd actually\n>    switch away from \"checkout\" (which we could still auto-route to new\n>    commands in perpetuity, but print a warning or error). Otherwise\n>    we'll just end up with https://xkcd.com/927/ and more UI confusion\n>    for all.\n\nHeh, that situation is only avoided when the new command has clear\nadvantages over the old, and ISTM that we can only compete on\nUX and better defaults, so maybe I'd push for making it more logical,\nmaybe so:\n\n  git tree-to-worktree # git checkout <commit> -- <path>\n  git index-to-worktree # git checkout -- <path>\n  git rev-to-ref # git checkout <commit>\n\nJust food for thought, specifically the last one would be\nhilarious if we'd end up with it.\n\nStefan\n"},{"id":"364109","messageId":"xmqqk1kzuzmg.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CAGZ79kaCidJ1s2vXaQX9b_o7Tk4O+WTdmBSM3RBKX3bCBMSFKA@mail.gmail.com","subject":"Re: [RFC] Introduce two new commands, switch-branch and restore-paths","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-27T00:34:47Z","receivedAt":"2018-11-27T00:34:57Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> Maybe we need to step back and consider what the command does.\n> And from that I would name it \"rewire-HEAD-and-update-index&worktree\"\n> and then simplify from there. For the beginner user, the concept of\n> HEAD might be overwhelming, such that we don't want to have that\n> in there.\n\nI'd have to say that it is totally backwards.\n\nUse of HEAD, ref, etc. is merely a means to what the end users want\nto achieve, which is to switch to a \"branch\" (in air quotes, as the\nword in this sentence means a bit broader than \"a ref that is in\nrefs/heads/\"), to choose which lineage of commits to work on to grow\nor reshape the history.  Most of the time, you would be working on a\nbranch (that is a ref that is in refs/heads/), sometimes you would\nbe working on an unnamed \"branch\" (i.e. detached HEAD state, only\ndifference between being on it and a normal branch is whether it is\nnamed---the history manipulation can happen exactly the same way).\n\nIn other words, your initial motivation of stepping back and\nthinking about what the command is about is very good.  But in the\ncontext of checking out a branch, the concept the end user works\nwith are at the level of \"branch\", not HEAD, ref, index, working\ntree (all of which are underlying implementation details that let\nyou work on manipulating the history, represented by the \"branch\").\n\n> \"content-to-path\", maybe(?) ...\n\nPath is not a place.  A path t/Makefile is a shared name of a place\nin a tree, the index, or in the working tree.  Renaming \"git add\" to\n\"content-to-path\" because it adds the content to the index at path\nmay be equally OK, but it misses the essense (which is that it is to\nadd to the index and not to anythng else like a tree or the working\ntree).\n\nI actually think the easiest-to-understand shorthand for the\noperation \"checking out the contents at the path to the working\ntree\" is \"checkout\".  \n\nThese...\n\n>   git tree-to-worktree # git checkout <commit> -- <path>\n>   git index-to-worktree # git checkout -- <path>\n\n...are interesting and worth learning lessons from.  These would be\nsomething people would suggest when users start making noises about\n\"restore-to-path\" or \"content-to-path\" overloads three different\noperations and is confusing.\n\nI think \"restore-to-path\" that can take contents for individual\npaths out of either a treeish or the index and update the index and\nthe working tree, depending on what the user tells it to do, is not\nconfusing to the end users.  At the conceptual level, the users need\nto have a mental model that has three places (tree-ish, index and\nworking tree) that can hold contents to make use of these\noperations, so it is only the matter of how to express it clearly\nand concisely.\n\nFor that matter, \"checkout <branch>\", \"checkout <treeish> -- <path>\"\nand \"checkout -- <path>\" already is a trio of clear and cocncise way\nto spell three distinct operations, so with the right mental model,\nit may not be so confusing to the users.  And \"checkout-branch\",\n\"checkout-contents-from-tree\", and \"checkout-contents-from-index\"\nlonghands may be a good way to help new users form the right mental\nmodel, as they are more explicit in their names; once they form the\nright mental model (that is, there are three places the contents\nlive, and there are a few operations to move contents from the two\nplaces to the working tree, i.e. what traditionally is called\n\"checking things out\"), they may gradulate to the shorthand form.\n\n"},{"id":"364139","messageId":"20181127165211.24763-1-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181120174554.GA29910@duynguyen.home","subject":"[PATCH/RFC v2 0/7] Introduce new commands switch-branch and checkout-files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-27T16:52:04Z","receivedAt":"2018-11-27T16:53:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v2 is just a bit better to look at than v1. This is by no means final.\nIf you think the command name is bad, the default behavior should\nchange, or something else, speak up. It's still very \"RFC\".\n\nv2 breaks down the giant patch in v1 and starts adding some changes in\nthese new commands:\n\n- restore-paths is renamed to checkout-paths. I wrote I didn't like\n  \"checkout\" because of completion conflict. But who am I kidding,\n  I'll use aliases anyway. \"-files\" instead of \"-paths\" because we\n  already have ls-files.\n- both commands will not accept no arguments. There is no \"git\n  checkout\" equivalent.\n- ambiguation rules are now aware that \"switch-branch\" for example\n  can't take pathspec...\n- the last patch tries to hide \"git checkout\" away. The command\n  example updates show how these will be used. Which probably helps\n  figure better names or defaults for them too\n\nOne thing I notice that we often use \"git checkout -- <path>\" and\nrarely \"git checkout <tree-ish> -- <path>\". Which makes me think\nperhaps \"git checkout-files\" should not use \"--\" to separate the two.\nWe'll have this instead\n\n    git checkout-files [--from=<tree-ish>] <paths>\n\n\nOh and of course I'll be waiting for the new --index from Thomas\nbefore submitting submitting any thing serious for 'next'. We still\nhave plenty of time.\n\nNguyễn Thái Ngọc Duy (7):\n  parse-options: allow parse_options_concat(NULL, options)\n  checkout: make \"opts\" in cmd_checkout() a pointer\n  checkout: move 'confict_style' to checkout_opts\n  checkout: move dwim_new_local_branch to checkout_opts\n  checkout: split options[] array in three pieces\n  checkout: split into switch-branch and checkout-files\n  Suggest other commands instead of \"git checkout\"\n\n Documentation/git-branch.txt           |   8 +-\n Documentation/git-check-ref-format.txt |   2 +-\n Documentation/git-format-patch.txt     |   2 +-\n Documentation/git-merge-base.txt       |   2 +-\n Documentation/git-rebase.txt           |   2 +-\n Documentation/git-remote.txt           |   2 +-\n Documentation/git-rerere.txt           |  10 +-\n Documentation/git-reset.txt            |  18 +-\n Documentation/git-revert.txt           |   2 +-\n Documentation/git-stash.txt            |   6 +-\n Documentation/gitattributes.txt        |   2 +-\n Documentation/gitcli.txt               |   4 +-\n Documentation/gitcore-tutorial.txt     |  18 +-\n Documentation/giteveryday.txt          |  24 +--\n Documentation/githooks.txt             |   5 +-\n Documentation/gittutorial-2.txt        |   2 +-\n Documentation/gittutorial.txt          |   4 +-\n Documentation/revisions.txt            |   2 +-\n Documentation/user-manual.txt          |  54 +++---\n advice.c                               |   2 +-\n builtin.h                              |   2 +\n builtin/checkout.c                     | 256 +++++++++++++++++--------\n git.c                                  |   2 +\n parse-options-cb.c                     |   2 +-\n sha1-name.c                            |   2 +-\n wt-status.c                            |   2 +-\n 26 files changed, 271 insertions(+), 166 deletions(-)\n\n-- \n2.19.1.1327.g328c130451.dirty\n\n"},{"id":"364140","messageId":"20181127165211.24763-2-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181127165211.24763-1-pclouds@gmail.com","subject":"[PATCH v2 1/7] parse-options: allow parse_options_concat(NULL, options)","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-27T16:52:05Z","receivedAt":"2018-11-27T16:53:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"There is currently no caller that calls this function with \"a\" being\nNULL. But it will be introduced shortly. It is used to construct the\noption array from scratch, e.g.\n\n   struct parse_options opts = NULL;\n   opts = parse_options_concat(opts, opts_1);\n   opts = parse_options_concat(opts, opts_2);\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n parse-options-cb.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex 8c9edce52f..c609d52926 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -126,7 +126,7 @@ struct option *parse_options_concat(struct option *a, struct option *b)\n \tstruct option *ret;\n \tsize_t i, a_len = 0, b_len = 0;\n \n-\tfor (i = 0; a[i].type != OPTION_END; i++)\n+\tfor (i = 0; a && a[i].type != OPTION_END; i++)\n \t\ta_len++;\n \tfor (i = 0; b[i].type != OPTION_END; i++)\n \t\tb_len++;\n-- \n2.19.1.1327.g328c130451.dirty\n\n"},{"id":"364141","messageId":"20181127165211.24763-3-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181127165211.24763-1-pclouds@gmail.com","subject":"[PATCH v2 2/7] checkout: make \"opts\" in cmd_checkout() a pointer","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-27T16:52:06Z","receivedAt":"2018-11-27T16:53:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"opts\" will soon be moved out of cmd_checkout(). To keep changes in\nthat patch smaller, convert \"opts\" to a pointer and keep the real\nthing behind \"real_opts\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 109 +++++++++++++++++++++++----------------------\n 1 file changed, 55 insertions(+), 54 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex acdafc6e4c..31245c1eb4 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1225,76 +1225,77 @@ static int checkout_branch(struct checkout_opts *opts,\n \n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n-\tstruct checkout_opts opts;\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tchar *conflict_style = NULL;\n \tint dwim_new_local_branch = 1;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n-\t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n-\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n-\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n-\t\tOPT_BOOL(0, \"detach\", &opts.force_detach, N_(\"detach HEAD at named commit\")),\n-\t\tOPT_SET_INT('t', \"track\",  &opts.track, N_(\"set upstream info for new branch\"),\n+\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n-\t\tOPT_STRING(0, \"orphan\", &opts.new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-\t\tOPT_SET_INT_F('2', \"ours\", &opts.writeout_stage,\n+\t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n-\t\tOPT_SET_INT_F('3', \"theirs\", &opts.writeout_stage,\n+\t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts.force, N_(\"force checkout (throw away local modifications)\"),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts.merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts.overwrite_ignore,\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n-\t\tOPT_BOOL('p', \"patch\", &opts.patch_mode, N_(\"select hunks interactively\")),\n-\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts.ignore_skipworktree,\n+\t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n \t\tOPT_HIDDEN_BOOL(0, \"guess\", &dwim_new_local_branch,\n \t\t\t\tN_(\"second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n \t\t\t    \"checkout\", \"control recursive updating of submodules\",\n \t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress reporting\")),\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_END(),\n \t};\n \n-\tmemset(&opts, 0, sizeof(opts));\n+\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n-\topts.overwrite_ignore = 1;\n-\topts.prefix = prefix;\n-\topts.show_progress = -1;\n+\topts->overwrite_ignore = 1;\n+\topts->prefix = prefix;\n+\topts->show_progress = -1;\n \n-\tgit_config(git_checkout_config, &opts);\n+\tgit_config(git_checkout_config, opts);\n \n-\topts.track = BRANCH_TRACK_UNSPECIFIED;\n+\topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tif (opts.show_progress < 0) {\n-\t\tif (opts.quiet)\n-\t\t\topts.show_progress = 0;\n+\tif (opts->show_progress < 0) {\n+\t\tif (opts->quiet)\n+\t\t\topts->show_progress = 0;\n \t\telse\n-\t\t\topts.show_progress = isatty(2);\n+\t\t\topts->show_progress = isatty(2);\n \t}\n \n \tif (conflict_style) {\n-\t\topts.merge = 1; /* implied */\n+\t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n \t}\n \n-\tif ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)\n+\tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n \n \t/*\n@@ -1302,14 +1303,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t * and new_branch_force and new_orphan_branch will tell us which one of\n \t * -b/-B/--orphan is being used.\n \t */\n-\tif (opts.new_branch_force)\n-\t\topts.new_branch = opts.new_branch_force;\n+\tif (opts->new_branch_force)\n+\t\topts->new_branch = opts->new_branch_force;\n \n-\tif (opts.new_orphan_branch)\n-\t\topts.new_branch = opts.new_orphan_branch;\n+\tif (opts->new_orphan_branch)\n+\t\topts->new_branch = opts->new_orphan_branch;\n \n \t/* --track without -b/-B/--orphan should DWIM */\n-\tif (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {\n+\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n \t\tconst char *argv0 = argv[0];\n \t\tif (!argc || !strcmp(argv0, \"--\"))\n \t\t\tdie(_(\"--track needs a branch name\"));\n@@ -1318,7 +1319,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\targv0 = strchr(argv0, '/');\n \t\tif (!argv0 || !argv0[1])\n \t\t\tdie(_(\"missing branch name; try -b\"));\n-\t\topts.new_branch = argv0 + 1;\n+\t\topts->new_branch = argv0 + 1;\n \t}\n \n \t/*\n@@ -1337,56 +1338,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tif (argc) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n-\t\t\t!opts.patch_mode &&\n+\t\t\t!opts->patch_mode &&\n \t\t\tdwim_new_local_branch &&\n-\t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n-\t\t\t!opts.new_branch;\n+\t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n+\t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &new_branch_info, opts, &rev,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\n \n \tif (argc) {\n-\t\tparse_pathspec(&opts.pathspec, 0,\n-\t\t\t       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n+\t\tparse_pathspec(&opts->pathspec, 0,\n+\t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n \t\t\t       prefix, argv);\n \n-\t\tif (!opts.pathspec.nr)\n+\t\tif (!opts->pathspec.nr)\n \t\t\tdie(_(\"invalid path specification\"));\n \n \t\t/*\n \t\t * Try to give more helpful suggestion.\n \t\t * new_branch && argc > 1 will be caught later.\n \t\t */\n-\t\tif (opts.new_branch && argc == 1)\n+\t\tif (opts->new_branch && argc == 1)\n \t\t\tdie(_(\"'%s' is not a commit and a branch '%s' cannot be created from it\"),\n-\t\t\t\targv[0], opts.new_branch);\n+\t\t\t\targv[0], opts->new_branch);\n \n-\t\tif (opts.force_detach)\n+\t\tif (opts->force_detach)\n \t\t\tdie(_(\"git checkout: --detach does not take a path argument '%s'\"),\n \t\t\t    argv[0]);\n \n-\t\tif (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)\n+\t\tif (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)\n \t\t\tdie(_(\"git checkout: --ours/--theirs, --force and --merge are incompatible when\\n\"\n \t\t\t      \"checking out of the index.\"));\n \t}\n \n-\tif (opts.new_branch) {\n+\tif (opts->new_branch) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tif (opts.new_branch_force)\n-\t\t\topts.branch_exists = validate_branchname(opts.new_branch, &buf);\n+\t\tif (opts->new_branch_force)\n+\t\t\topts->branch_exists = validate_branchname(opts->new_branch, &buf);\n \t\telse\n-\t\t\topts.branch_exists =\n-\t\t\t\tvalidate_new_branchname(opts.new_branch, &buf, 0);\n+\t\t\topts->branch_exists =\n+\t\t\t\tvalidate_new_branchname(opts->new_branch, &buf, 0);\n \t\tstrbuf_release(&buf);\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr) {\n-\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\tif (opts->patch_mode || opts->pathspec.nr) {\n+\t\tint ret = checkout_paths(opts, new_branch_info.name);\n \t\tif (ret && dwim_remotes_matched > 1 &&\n \t\t    advice_checkout_ambiguous_remote_branch_name)\n \t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n@@ -1405,6 +1406,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n-\t\treturn checkout_branch(&opts, &new_branch_info);\n+\t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n-- \n2.19.1.1327.g328c130451.dirty\n\n"},{"id":"364142","messageId":"20181127165211.24763-6-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181127165211.24763-1-pclouds@gmail.com","subject":"[PATCH v2 5/7] checkout: split options[] array in three pieces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-27T16:52:09Z","receivedAt":"2018-11-27T16:53:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a preparation step for introducing new commands that do parts\nof what checkout does. There will be two new commands, one is about\nswitching branches, detaching HEAD... one about checking out\npaths. These share the a subset of command line options. The rest of\ncommand line options are separate.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 80 ++++++++++++++++++++++++++++++++--------------\n 1 file changed, 56 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex a50c51f287..d9dbd2d40d 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1226,14 +1226,32 @@ static int checkout_branch(struct checkout_opts *opts,\n \treturn switch_branches(opts, new_branch_info);\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static struct option *add_common_options(struct checkout_opts *opts,\n+\t\t\t\t\t struct option *prevopts)\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n-\tstruct branch_info new_branch_info;\n-\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n+\t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n+\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n+\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n+\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n+\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_switch_branch_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n@@ -1243,33 +1261,43 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_HIDDEN_BOOL(0, \"guess\", &opts->dwim_new_local_branch,\n+\t\t\t\tN_(\"second guess 'git checkout <no-such-branch>'\")),\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n+\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_checkout_path_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n \t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n-\t\t\t   N_(\"update ignored files (default)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n-\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n-\t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_HIDDEN_BOOL(0, \"guess\", &opts->dwim_new_local_branch,\n-\t\t\t\tN_(\"second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n-\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n-\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n-\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n-\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n-\t\tOPT_END(),\n+\t\tOPT_END()\n \t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n+\tstruct branch_info new_branch_info;\n+\tint dwim_remotes_matched = 0;\n+\tstruct option *options = NULL;\n \n \tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n@@ -1282,6 +1310,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n+\toptions = add_common_options(opts, options);\n+\toptions = add_switch_branch_options(opts, options);\n+\toptions = add_checkout_path_options(opts, options);\n+\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-- \n2.19.1.1327.g328c130451.dirty\n\n"},{"id":"364143","messageId":"20181127165211.24763-7-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181127165211.24763-1-pclouds@gmail.com","subject":"[PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-27T16:52:10Z","receivedAt":"2018-11-27T16:53:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" doing too many things is a source of confusion for many\nusers (and it even bites old timers sometimes). To rememdy that, the\ncommand is now split in two: switch-branch and checkout-files.\n\nThe switch-branch command is all about switching branches, detaching,\nDWIM-ing new branch... It does not accept pathspecs and it always\nrequires a ref (in contrast, \"git checkout\" without arguments works)\n\nThe checkout-files command on the other hand is all about resetting\ncertain files in worktree, either from the index or from a specific\ntree. It could accept a tree-ish, but it will never touch HEAD or the\nref it points to.\n\nThe good old \"git checkout\" command is still here and will be until\nall (or most of users) are sick of it.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin.h          |  2 +\n builtin/checkout.c | 91 +++++++++++++++++++++++++++++++++++++++-------\n git.c              |  2 +\n 3 files changed, 82 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..d4a66e5f79 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -138,6 +138,7 @@ extern int cmd_branch(int argc, const char **argv, const char *prefix);\n extern int cmd_bundle(int argc, const char **argv, const char *prefix);\n 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_files(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@@ -227,6 +228,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n+extern int cmd_switch_branch(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_tag(int argc, const char **argv, const char *prefix);\n extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex d9dbd2d40d..c09d2da47a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -33,6 +33,16 @@ static const char * const checkout_usage[] = {\n \tNULL,\n };\n \n+static const char * const switch_branch_usage[] = {\n+\tN_(\"git switch-branch [<options>] <branch>\"),\n+\tNULL,\n+};\n+\n+static const char * const checkout_files_usage[] = {\n+\tN_(\"git checkout-files [<options>] [<branch>] -- <file>...\"),\n+\tNULL,\n+};\n+\n struct checkout_opts {\n \tint patch_mode;\n \tint quiet;\n@@ -45,6 +55,8 @@ struct checkout_opts {\n \tint ignore_other_worktrees;\n \tint show_progress;\n \tint dwim_new_local_branch;\n+\tint accept_pathspec;\n+\tint empty_arg_ok;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1056,7 +1068,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n \targ = argv[0];\n \tdash_dash_pos = -1;\n \tfor (i = 0; i < argc; i++) {\n-\t\tif (!strcmp(argv[i], \"--\")) {\n+\t\tif (opts->accept_pathspec && !strcmp(argv[i], \"--\")) {\n \t\t\tdash_dash_pos = i;\n \t\t\tbreak;\n \t\t}\n@@ -1067,6 +1079,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\thas_dash_dash = 1; /* case (3) or (1) */\n \telse if (dash_dash_pos >= 2)\n \t\tdie(_(\"only one reference expected, %d given.\"), dash_dash_pos);\n+\telse if (!opts->accept_pathspec)\n+\t\thas_dash_dash = 1;\n \n \tif (!strcmp(arg, \"-\"))\n \t\targ = \"@{-1}\";\n@@ -1291,30 +1305,23 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static int checkout_main(int argc, const char **argv, const char *prefix,\n+\t\t\t struct checkout_opts *opts, struct option *options,\n+\t\t\t const char * const usagestr[])\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n-\tstruct option *options = NULL;\n \n-\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n-\topts->dwim_new_local_branch = 1;\n \n \tgit_config(git_checkout_config, opts);\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\toptions = add_common_options(opts, options);\n-\toptions = add_switch_branch_options(opts, options);\n-\toptions = add_checkout_path_options(opts, options);\n-\n-\targc = parse_options(argc, argv, prefix, options, checkout_usage,\n+\targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tif (opts->show_progress < 0) {\n@@ -1381,7 +1388,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n-\t}\n+\t} else if (!opts->empty_arg_ok)\n+\t\tusage_with_options(usagestr, options);\n \n \tif (argc) {\n \t\tparse_pathspec(&opts->pathspec, 0,\n@@ -1443,3 +1451,60 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.dwim_new_local_branch = 1;\n+\topts.accept_pathspec = 1;\n+\topts.empty_arg_ok = 1;\n+\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, checkout_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\n+\n+int cmd_switch_branch(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.dwim_new_local_branch = 1;\n+\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, switch_branch_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\n+\n+int cmd_checkout_files(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.accept_pathspec = 1;\n+\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, checkout_files_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\ndiff --git a/git.c b/git.c\nindex 2f604a41ea..3b86ba765c 100644\n--- a/git.c\n+++ b/git.c\n@@ -457,6 +457,7 @@ static struct cmd_struct commands[] = {\n \t{ \"check-mailmap\", cmd_check_mailmap, RUN_SETUP },\n \t{ \"check-ref-format\", cmd_check_ref_format, NO_PARSEOPT  },\n \t{ \"checkout\", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"checkout-files\", cmd_checkout_files, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"checkout-index\", cmd_checkout_index,\n \t\tRUN_SETUP | NEED_WORK_TREE},\n \t{ \"cherry\", cmd_cherry, RUN_SETUP },\n@@ -557,6 +558,7 @@ static struct cmd_struct commands[] = {\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n+\t{ \"switch-branch\", cmd_switch_branch, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n \t{ \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n \t{ \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\n-- \n2.19.1.1327.g328c130451.dirty\n\n"},{"id":"364144","messageId":"20181127165211.24763-5-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181127165211.24763-1-pclouds@gmail.com","subject":"[PATCH v2 4/7] checkout: move dwim_new_local_branch to checkout_opts","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-27T16:52:08Z","receivedAt":"2018-11-27T16:53:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 8 +++++---\n 1 file changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 211a347a0c..a50c51f287 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -44,6 +44,8 @@ struct checkout_opts {\n \tint ignore_skipworktree;\n \tint ignore_other_worktrees;\n \tint show_progress;\n+\tint dwim_new_local_branch;\n+\n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n \t * should be updated accordingly.\n@@ -1229,7 +1231,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct checkout_opts real_opts;\n \tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n-\tint dwim_new_local_branch = 1;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n@@ -1259,7 +1260,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_HIDDEN_BOOL(0, \"guess\", &dwim_new_local_branch,\n+\t\tOPT_HIDDEN_BOOL(0, \"guess\", &opts->dwim_new_local_branch,\n \t\t\t\tN_(\"second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n@@ -1275,6 +1276,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n+\topts->dwim_new_local_branch = 1;\n \n \tgit_config(git_checkout_config, opts);\n \n@@ -1339,7 +1341,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n \t\t\t!opts->patch_mode &&\n-\t\t\tdwim_new_local_branch &&\n+\t\t\topts->dwim_new_local_branch &&\n \t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n \t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-- \n2.19.1.1327.g328c130451.dirty\n\n"},{"id":"364145","messageId":"20181127165211.24763-8-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181127165211.24763-1-pclouds@gmail.com","subject":"[PATCH v2 7/7] Suggest other commands instead of \"git checkout\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-27T16:52:11Z","receivedAt":"2018-11-27T16:53:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The assumption made is here\n\n- \"git checkout\" is a horrible monster that should only be touched\n  with a two-meter pole\n\n- there are other commands that can achieve the same thing\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-branch.txt           |  8 ++--\n Documentation/git-check-ref-format.txt |  2 +-\n Documentation/git-format-patch.txt     |  2 +-\n Documentation/git-merge-base.txt       |  2 +-\n Documentation/git-rebase.txt           |  2 +-\n Documentation/git-remote.txt           |  2 +-\n Documentation/git-rerere.txt           | 10 ++---\n Documentation/git-reset.txt            | 18 ++++-----\n Documentation/git-revert.txt           |  2 +-\n Documentation/git-stash.txt            |  6 +--\n Documentation/gitattributes.txt        |  2 +-\n Documentation/gitcli.txt               |  4 +-\n Documentation/gitcore-tutorial.txt     | 18 ++++-----\n Documentation/giteveryday.txt          | 24 ++++++------\n Documentation/githooks.txt             |  5 ++-\n Documentation/gittutorial-2.txt        |  2 +-\n Documentation/gittutorial.txt          |  4 +-\n Documentation/revisions.txt            |  2 +-\n Documentation/user-manual.txt          | 54 +++++++++++++-------------\n advice.c                               |  2 +-\n sha1-name.c                            |  2 +-\n wt-status.c                            |  2 +-\n 22 files changed, 88 insertions(+), 87 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex bf5316ffa9..1564df47d2 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>\n which points to the current `HEAD`, or <start-point> if given.\n \n Note that this will create the new branch, but it will not switch the\n-working tree to it; use \"git checkout <newbranch>\" to switch to the\n+working tree to it; use \"git switch-branch <newbranch>\" to switch to the\n new branch.\n \n When a local branch is started off a remote-tracking branch, Git sets up the\n@@ -194,7 +194,7 @@ This option is only applicable in non-verbose mode.\n +\n This behavior is the default when the start point is a remote-tracking branch.\n Set the branch.autoSetupMerge configuration variable to `false` if you\n-want `git checkout` and `git branch` to always behave as if `--no-track`\n+want `git switch-branch` and `git branch` to always behave as if `--no-track`\n were given. Set it to `always` if you want this behavior when the\n start-point is either a local or remote-tracking branch.\n \n@@ -293,7 +293,7 @@ Start development from a known tag::\n $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6\n $ cd my2.6\n $ git branch my2.6.14 v2.6.14   <1>\n-$ git checkout my2.6.14\n+$ git switch-branch my2.6.14\n ------------\n +\n <1> This step and the next one could be combined into a single step with\n@@ -319,7 +319,7 @@ NOTES\n -----\n \n If you are creating a branch that you want to checkout immediately, it is\n-easier to use the git checkout command with its `-b` option to create\n+easier to use the \"git switch-branch\" command with its `-b` option to create\n a branch and check it out with a single command.\n \n The options `--contains`, `--no-contains`, `--merged` and `--no-merged`\ndiff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt\nindex d9de992585..38c2169d7a 100644\n--- a/Documentation/git-check-ref-format.txt\n+++ b/Documentation/git-check-ref-format.txt\n@@ -88,7 +88,7 @@ but it is explicitly forbidden at the beginning of a branch name).\n When run with `--branch` option in a repository, the input is first\n expanded for the ``previous checkout syntax''\n `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that\n-was checked out using \"git checkout\" operation. This option should be\n+was checked out using \"git switch-branch\" operation. This option should be\n used by porcelains to accept this syntax anywhere a branch name is\n expected, so they can act as if you typed the branch name. As an\n exception note that, the ``previous checkout operation'' might result\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex aba4c5febe..0ceaa1173c 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -416,7 +416,7 @@ One way to test if your MUA is set up correctly is:\n * Apply it:\n \n     $ git fetch <project> master:test-apply\n-    $ git checkout test-apply\n+    $ git switch-branch test-apply\n     $ git reset --hard\n     $ git am a.patch\n \ndiff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt\nindex 9f07f4f6ed..1b25e5d530 100644\n--- a/Documentation/git-merge-base.txt\n+++ b/Documentation/git-merge-base.txt\n@@ -149,7 +149,7 @@ instead.\n Discussion on fork-point mode\n -----------------------------\n \n-After working on the `topic` branch created with `git checkout -b\n+After working on the `topic` branch created with `git switch-branch -b\n topic origin/master`, the history of remote-tracking branch\n `origin/master` may have been rewound and rebuilt, leading to a\n history of this shape:\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 80793bad8d..fe10880633 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -17,7 +17,7 @@ SYNOPSIS\n DESCRIPTION\n -----------\n If <branch> is specified, 'git rebase' will perform an automatic\n-`git checkout <branch>` before doing anything else.  Otherwise\n+`git switch-branch <branch>` before doing anything else.  Otherwise\n it remains on the current branch.\n \n If <upstream> is not specified, the upstream configured in\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 0cad37fb81..044bbdb27c 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -230,7 +230,7 @@ $ git branch -r\n   staging/master\n   staging/staging-linus\n   staging/staging-next\n-$ git checkout -b staging staging/master\n+$ git switch-branch -b staging staging/master\n ...\n ------------\n \ndiff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt\nindex df310d2a58..fe9d21b395 100644\n--- a/Documentation/git-rerere.txt\n+++ b/Documentation/git-rerere.txt\n@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.\n One way to do it is to pull master into the topic branch:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch-branch topic\n \t$ git merge master\n \n               o---*---o---+ topic\n@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,\n in which case the final commit graph would look like this:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch-branch topic\n \t$ git merge master\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch-branch master\n \t$ git merge topic\n \n               o---*---o---+---o---o topic\n@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on\n top of the tip before the test merge:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch-branch topic\n \t$ git merge master\n \t$ git reset --hard HEAD^ ;# rewind the test merge\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch-branch master\n \t$ git merge topic\n \n               o---*---o-------o---o topic\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex 2dac95c71a..ca46b4c967 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -149,9 +149,9 @@ See also the --amend option to linkgit:git-commit[1].\n Undo a commit, making it a topic branch::\n +\n ------------\n-$ git branch topic/wip     <1>\n-$ git reset --hard HEAD~3  <2>\n-$ git checkout topic/wip   <3>\n+$ git branch topic/wip          <1>\n+$ git reset --hard HEAD~3       <2>\n+$ git switch-branch topic/wip   <3>\n ------------\n +\n <1> You have made some commits, but realize they were premature\n@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you\n need to get to the other branch for a quick bugfix.\n +\n ------------\n-$ git checkout feature ;# you were working in \"feature\" branch and\n+$ git switch-branch feature ;# you were working in \"feature\" branch and\n $ work work work       ;# got interrupted\n $ git commit -a -m \"snapshot WIP\"                 <1>\n-$ git checkout master\n+$ git switch-branch master\n $ fix fix fix\n $ git commit ;# commit with real log\n-$ git checkout feature\n+$ git switch-branch feature\n $ git reset --soft HEAD^ ;# go back to WIP state  <2>\n $ git reset                                       <3>\n ------------\n@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.\n +\n ------------\n $ git tag start\n-$ git checkout -b branch1\n+$ git switch-branch -b branch1\n $ edit\n $ git commit ...                            <1>\n $ edit\n-$ git checkout -b branch2                   <2>\n+$ git switch-branch -b branch2              <2>\n $ git reset --keep start                    <3>\n ------------\n +\n <1> This commits your first edits in branch1.\n <2> In the ideal world, you could have realized that the earlier\n     commit did not belong to the new topic when you created and switched\n-    to branch2 (i.e. \"git checkout -b branch2 start\"), but nobody is\n+    to branch2 (i.e. \"git switch-branch -b branch2 start\"), but nobody is\n     perfect.\n <3> But you can use \"reset --keep\" to remove the unwanted commit after\n     you switched to \"branch2\".\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 837707a8fd..e49dbbec83 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -26,7 +26,7 @@ effect of some earlier commits (often only a faulty one).  If you want to\n throw away all uncommitted changes in your working directory, you\n should see linkgit:git-reset[1], particularly the `--hard` option.  If\n you want to extract specific files as they were in another commit, you\n-should see linkgit:git-checkout[1], specifically the `git checkout\n+should see linkgit:git-checkout[1], specifically the `git checkout-files\n <commit> -- <filename>` syntax.  Take care with these alternatives as\n both will discard uncommitted changes in your working directory.\n \ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 7ef8c47911..ea226979b1 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:\n +\n ----------------------------------------------------------------\n # ... hack hack hack ...\n-$ git checkout -b my_wip\n+$ git switch-branch -b my_wip\n $ git commit -a -m \"WIP\"\n-$ git checkout master\n+$ git switch-branch master\n $ edit emergency fix\n $ git commit -a -m \"Fix in a hurry\"\n-$ git checkout my_wip\n+$ git switch-branch my_wip\n $ git reset --soft HEAD^\n # ... continue hacking ...\n ----------------------------------------------------------------\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex b8392fc330..df62bd8019 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -112,7 +112,7 @@ Checking-out and checking-in\n \n These attributes affect how the contents stored in the\n repository are copied to the working tree files when commands\n-such as 'git checkout' and 'git merge' run.  They also affect how\n+such as 'git switch-branch' and 'git merge' run.  They also affect how\n Git stores the contents you prepare in the working tree in the\n repository upon 'git add' and 'git commit'.\n \ndiff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt\nindex 592e06d839..0ad4869f2c 100644\n--- a/Documentation/gitcli.txt\n+++ b/Documentation/gitcli.txt\n@@ -47,8 +47,8 @@ disambiguating `--` at appropriate places.\n    things:\n +\n --------------------------------\n-$ git checkout -- *.c\n-$ git checkout -- \\*.c\n+$ git checkout-files -- *.c\n+$ git checkout-files -- \\*.c\n --------------------------------\n +\n The former lets your shell expand the fileglob, and you are asking\ndiff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt\nindex e29a9effcc..49a8b5aa52 100644\n--- a/Documentation/gitcore-tutorial.txt\n+++ b/Documentation/gitcore-tutorial.txt\n@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just\n saying that you want to check out a new branch:\n \n ------------\n-$ git checkout -b mybranch\n+$ git branch mybranch\n ------------\n \n will create a new branch based at the current `HEAD` position, and switch\n@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.\n In other words, if you have an earlier tag or branch, you'd just do\n \n ------------\n-$ git checkout -b mybranch earlier-commit\n+$ git switch-branch -b mybranch earlier-commit\n ------------\n \n and it would create the new branch `mybranch` at the earlier commit,\n@@ -765,7 +765,7 @@ and check out the state at that time.\n You can always just jump back to your original `master` branch by doing\n \n ------------\n-$ git checkout master\n+$ git switch-branch master\n ------------\n \n (or any other branch-name, for that matter) and if you forget which\n@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]\n \n which will simply _create_ the branch, but will not do anything further.\n You can then later -- once you decide that you want to actually develop\n-on that branch -- switch to that branch with a regular 'git checkout'\n+on that branch -- switch to that branch with a regular 'git switch-branch\n with the branchname as the argument.\n \n \n@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in\n that branch, and do some work there.\n \n ------------------------------------------------\n-$ git checkout mybranch\n+$ git switch-branch mybranch\n $ echo \"Work, work, work\" >>hello\n $ git commit -m \"Some work.\" -i hello\n ------------------------------------------------\n@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back\n to the master branch, and editing the same file differently there:\n \n ------------\n-$ git checkout master\n+$ git switch-branch master\n ------------\n \n Here, take a moment to look at the contents of `hello`, and notice how they\n@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run\n 'git merge' to get the \"upstream changes\" back to your branch.\n \n ------------\n-$ git checkout mybranch\n+$ git switch-branch mybranch\n $ git merge -m \"Merge upstream changes.\" master\n ------------\n \n@@ -1133,9 +1133,9 @@ Remember, before running 'git merge', our `master` head was at\n work.\" commit.\n \n ------------\n-$ git checkout mybranch\n+$ git switch-branch mybranch\n $ git reset --hard master^2\n-$ git checkout master\n+$ git switch-branch master\n $ git reset --hard master^\n ------------\n \ndiff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\nindex 9f2528fc8c..861b2bb616 100644\n--- a/Documentation/giteveryday.txt\n+++ b/Documentation/giteveryday.txt\n@@ -80,9 +80,9 @@ $ git tag v2.43 <2>\n Create a topic branch and develop.::\n +\n ------------\n-$ git checkout -b alsa-audio <1>\n+$ git branch alsa-audio <1>\n $ edit/compile/test\n-$ git checkout -- curses/ux_audio_oss.c <2>\n+$ git checkout-files -- curses/ux_audio_oss.c <2>\n $ git add curses/ux_audio_alsa.c <3>\n $ edit/compile/test\n $ git diff HEAD <4>\n@@ -90,7 +90,7 @@ $ git commit -a -s <5>\n $ edit/compile/test\n $ git diff HEAD^ <6>\n $ git commit -a --amend <7>\n-$ git checkout master <8>\n+$ git switch-branch master <8>\n $ git merge alsa-audio <9>\n $ git log --since='3 days ago' <10>\n $ git log v2.43.. curses/ <11>\n@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::\n ------------\n $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6\n $ cd my2.6\n-$ git checkout -b mine master <1>\n+$ git switch-branch -b mine master <1>\n $ edit/compile/test; git commit -a -s <2>\n $ git format-patch master <3>\n $ git send-email --to=\"person <email@example.com>\" 00*.patch <4>\n-$ git checkout master <5>\n+$ git switch-branch master <5>\n $ git pull <6>\n $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>\n $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>\n@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit\n satellite$ git push origin <4>\n \n mothership$ cd frotz\n-mothership$ git checkout master\n+mothership$ git switch-branch master\n mothership$ git merge satellite/master <5>\n ------------\n +\n@@ -216,7 +216,7 @@ machine into the master branch.\n Branch off of a specific tag.::\n +\n ------------\n-$ git checkout -b private2.6.14 v2.6.14 <1>\n+$ git switch-branch -b private2.6.14 v2.6.14 <1>\n $ edit/compile/test; git commit -a\n $ git checkout master\n $ git cherry-pick v2.6.14..private2.6.14 <2>\n@@ -274,14 +274,14 @@ $ mailx <3>\n & s 2 3 4 5 ./+to-apply\n & s 7 8 ./+hold-linus\n & q\n-$ git checkout -b topic/one master\n+$ git switch-branch -b topic/one master\n $ git am -3 -i -s ./+to-apply <4>\n $ compile/test\n-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>\n-$ git checkout topic/one && git rebase master <6>\n-$ git checkout pu && git reset --hard next <7>\n+$ git switch-branch -b hold/linus && git am -3 -i -s ./+hold-linus <5>\n+$ git switch-branch topic/one && git rebase master <6>\n+$ git switch-branch pu && git reset --hard next <7>\n $ git merge topic/one topic/two && git merge hold/linus <8>\n-$ git checkout maint\n+$ git switch-branch maint\n $ git cherry-pick master~4 <9>\n $ compile/test\n $ git tag -s -m \"GIT 0.99.9x\" v0.99.9x <10>\ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 959044347e..3939ec774a 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -166,7 +166,7 @@ worktree.  The hook is given three parameters: the ref of the previous HEAD,\n the ref of the new HEAD (which may or may not have changed), and a flag\n indicating whether the checkout was a branch checkout (changing branches,\n flag=1) or a file checkout (retrieving a file from the index, flag=0).\n-This hook cannot affect the outcome of `git checkout`.\n+This hook cannot affect the outcome of `git switch-branch` or `git checkout`.\n \n It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is\n used. The first parameter given to the hook is the null-ref, the second the\n@@ -402,7 +402,8 @@ exit with a zero status.\n For example, the hook can simply run `git read-tree -u -m HEAD \"$1\"`\n in order to emulate `git fetch` that is run in the reverse direction\n with `git push`, as the two-tree form of `git read-tree -u -m` is\n-essentially the same as `git checkout` that switches branches while\n+essentially the same as `git switch-branch` or `git checkout`\n+that switches branches while\n keeping the local changes in the working tree that do not interfere\n with the difference between the branches.\n \ndiff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt\nindex e0976f6017..125213d951 100644\n--- a/Documentation/gittutorial-2.txt\n+++ b/Documentation/gittutorial-2.txt\n@@ -376,7 +376,7 @@ Changes to be committed:\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git checkout-files -- <file>...\" to discard changes in working directory)\n \n \tmodified:   file.txt\n \ndiff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\nindex 242de31cb6..396e55c191 100644\n--- a/Documentation/gittutorial.txt\n+++ b/Documentation/gittutorial.txt\n@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;\n type\n \n ------------------------------------------------\n-$ git checkout experimental\n+$ git switch-branch experimental\n ------------------------------------------------\n \n to switch to the experimental branch.  Now edit a file, commit the\n@@ -216,7 +216,7 @@ change, and switch back to the master branch:\n ------------------------------------------------\n (edit file)\n $ git commit -a\n-$ git checkout master\n+$ git switch-branch master\n ------------------------------------------------\n \n Check that the change you made is no longer visible, since it was\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 72daa20e76..f55502cd50 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -115,7 +115,7 @@ Here's an example to make it more clear:\n ------------------------------\n $ git config push.default current\n $ git config remote.pushdefault myfork\n-$ git checkout -b mybranch origin/master\n+$ git switch-branch -b mybranch origin/master\n \n $ git rev-parse --symbolic-full-name @{upstream}\n refs/remotes/origin/master\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex eff7890274..56397e93d4 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -125,7 +125,7 @@ Create a new branch head pointing to one of these versions and check it\n out using linkgit:git-checkout[1]:\n \n ------------------------------------------------\n-$ git checkout -b new v2.6.13\n+$ git switch-branch -b new v2.6.13\n ------------------------------------------------\n \n The working directory then reflects the contents that the project had\n@@ -282,10 +282,10 @@ a summary of the commands:\n \tthis command will fail with a warning.\n `git branch -D <branch>`::\n \tdelete the branch `<branch>` irrespective of its merged status.\n-`git checkout <branch>`::\n+`git switch-branch <branch>`::\n \tmake the current branch `<branch>`, updating the working\n \tdirectory to reflect the version referenced by `<branch>`.\n-`git checkout -b <new> <start-point>`::\n+`git switch-branch -b <new> <start-point>`::\n \tcreate a new branch `<new>` referencing `<start-point>`, and\n \tcheck it out.\n \n@@ -302,12 +302,12 @@ ref: refs/heads/master\n Examining an old version without creating a new branch\n ------------------------------------------------------\n \n-The `git checkout` command normally expects a branch head, but will also\n+The `git switch-branch` command normally expects a branch head, but will also\n accept an arbitrary commit; for example, you can check out the commit\n referenced by a tag:\n \n ------------------------------------------------\n-$ git checkout v2.6.17\n+$ git switch-branch v2.6.17\n Note: checking out 'v2.6.17'.\n \n You are in 'detached HEAD' state. You can look around, make experimental\n@@ -317,7 +317,7 @@ state without impacting any branches by performing another checkout.\n If you want to create a new branch to retain commits you create, you may\n do so (now or later) by using -b with the checkout command again. Example:\n \n-  git checkout -b new_branch_name\n+  git switch-branch -b new_branch_name\n \n HEAD is now at 427abfa Linux v2.6.17\n ------------------------------------------------\n@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches\n on a branch of your own, just as you would for a tag:\n \n ------------------------------------------------\n-$ git checkout -b my-todo-copy origin/todo\n+$ git switch-branch -b my-todo-copy origin/todo\n ------------------------------------------------\n \n You can also check out `origin/todo` directly to examine it or\n@@ -1523,12 +1523,12 @@ Checking out an old version of a file\n \n In the process of undoing a previous bad change, you may find it\n useful to check out an older version of a particular file using\n-linkgit:git-checkout[1].  We've used `git checkout` before to switch\n+linkgit:git-checkout[1].  We've used `git switch-branch` before to switch\n branches, but it has quite different behavior if it is given a path\n name: the command\n \n -------------------------------------------------\n-$ git checkout HEAD^ path/to/file\n+$ git checkout-files HEAD^ path/to/file\n -------------------------------------------------\n \n replaces path/to/file by the contents it had in the commit HEAD^, and\n@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master\n These can be easily kept up to date using linkgit:git-pull[1].\n \n -------------------------------------------------\n-$ git checkout test && git pull\n-$ git checkout release && git pull\n+$ git switch-branch test && git pull\n+$ git switch-branch release && git pull\n -------------------------------------------------\n \n Important note!  If you have any local changes in these branches, then\n@@ -2264,7 +2264,7 @@ tested changes\n 2) help future bug hunters that use `git bisect` to find problems\n \n -------------------------------------------------\n-$ git checkout -b speed-up-spinlocks v2.6.35\n+$ git switch-branch -b speed-up-spinlocks v2.6.35\n -------------------------------------------------\n \n Now you apply the patch(es), run some tests, and commit the change(s).  If\n@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the\n \"test\" branch in preparation to make it public:\n \n -------------------------------------------------\n-$ git checkout test && git merge speed-up-spinlocks\n+$ git switch-branch test && git merge speed-up-spinlocks\n -------------------------------------------------\n \n It is unlikely that you would have any conflicts here ... but you might if you\n@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It\n means that the patches can be moved into the `release` tree in any order.\n \n -------------------------------------------------\n-$ git checkout release && git merge speed-up-spinlocks\n+$ git switch-branch release && git merge speed-up-spinlocks\n -------------------------------------------------\n \n After a while, you will have a number of branches, and despite the\n@@ -2358,7 +2358,7 @@ Here are some of the scripts that simplify all this even further.\n \n case \"$1\" in\n test|release)\n-\tgit checkout $1 && git pull . origin\n+\tgit switch-branch $1 && git pull . origin\n \t;;\n origin)\n \tbefore=$(git rev-parse refs/remotes/origin/master)\n@@ -2400,7 +2400,7 @@ test|release)\n \t\techo $1 already merged into $2 1>&2\n \t\texit 1\n \tfi\n-\tgit checkout $2 && git pull . $1\n+\tgit switch-branch $2 && git pull . $1\n \t;;\n *)\n \tusage\n@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch\n `origin`, and create some commits on top of it:\n \n -------------------------------------------------\n-$ git checkout -b mywork origin\n+$ git switch-branch -b mywork origin\n $ vi file.txt\n $ git commit\n $ vi otherfile.txt\n@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use\n linkgit:git-rebase[1]:\n \n -------------------------------------------------\n-$ git checkout mywork\n+$ git switch-branch mywork\n $ git rebase origin\n -------------------------------------------------\n \n@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the\n new commit:\n \n -------------------------------------------------\n-$ git checkout master\n+$ git switch-branch master\n -------------------------------------------------\n \n or\n \n -------------------------------------------------\n-$ git checkout -b fix-up\n+$ git switch-branch -b fix-up\n -------------------------------------------------\n \n then\n@@ -4194,7 +4194,7 @@ start.\n A good place to start is with the contents of the initial commit, with:\n \n ----------------------------------------------------\n-$ git checkout e83c5163\n+$ git switch-branch e83c5163\n ----------------------------------------------------\n \n The initial revision lays the foundation for almost everything Git has\n@@ -4437,10 +4437,10 @@ Managing branches\n -----------------\n \n -----------------------------------------------\n-$ git branch\t     # list all local branches in this repo\n-$ git checkout test  # switch working directory to branch \"test\"\n-$ git branch new     # create branch \"new\" starting at current HEAD\n-$ git branch -d new  # delete branch \"new\"\n+$ git branch\t\t\t# list all local branches in this repo\n+$ git switch-branch test\t# switch working directory to branch \"test\"\n+$ git branch new\t\t# create branch \"new\" starting at current HEAD\n+$ git branch -d new\t\t# delete branch \"new\"\n -----------------------------------------------\n \n Instead of basing a new branch on current HEAD (the default), use:\n@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch \"test\"\n Create and switch to a new branch at the same time:\n \n -----------------------------------------------\n-$ git checkout -b new v2.6.15\n+$ git switch-branch -b new v2.6.15\n -----------------------------------------------\n \n Update and examine branches from the repository you cloned from:\n@@ -4467,7 +4467,7 @@ $ git branch -r\t\t# list\n   origin/master\n   origin/next\n   ...\n-$ git checkout -b masterwork origin/master\n+$ git switch-branch -b masterwork origin/master\n -----------------------------------------------\n \n Fetch a branch from a different repository, and give it a new\ndiff --git a/advice.c b/advice.c\nindex 5f35656409..1befdb2163 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -195,7 +195,7 @@ void detach_advice(const char *new_name)\n \t\"state without impacting any branches by performing another checkout.\\n\\n\"\n \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n \t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n-\t\"  git checkout -b <new-branch-name>\\n\\n\");\n+\t\"  git branch <new-branch-name>\\n\\n\");\n \n \tfprintf(stderr, fmt, new_name);\n }\ndiff --git a/sha1-name.c b/sha1-name.c\nindex faa60f69e3..4e4e14a45c 100644\n--- a/sha1-name.c\n+++ b/sha1-name.c\n@@ -771,7 +771,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,\n \t\"because it will be ignored when you just specify 40-hex. These refs\\n\"\n \t\"may be created by mistake. For example,\\n\"\n \t\"\\n\"\n-\t\"  git checkout -b $br $(git rev-parse ...)\\n\"\n+\t\"  git switch-branch -b $br $(git rev-parse ...)\\n\"\n \t\"\\n\"\n \t\"where \\\"$br\\\" is somehow empty and a 40-hex ref is created. Please\\n\"\n \t\"examine these refs and maybe delete them. Turn this message off by\\n\"\ndiff --git a/wt-status.c b/wt-status.c\nindex a24711374c..6266683926 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -224,7 +224,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s,\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git add <file>...\\\" to update what will be committed)\"));\n \telse\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git add/rm <file>...\\\" to update what will be committed)\"));\n-\tstatus_printf_ln(s, c, _(\"  (use \\\"git checkout -- <file>...\\\" to discard changes in working directory)\"));\n+\tstatus_printf_ln(s, c, _(\"  (use \\\"git checkout-files <file>...\\\" to discard changes in working directory)\"));\n \tif (has_dirty_submodules)\n \t\tstatus_printf_ln(s, c, _(\"  (commit or discard the untracked or modified content in submodules)\"));\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n-- \n2.19.1.1327.g328c130451.dirty\n\n"},{"id":"364146","messageId":"20181127165211.24763-4-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181127165211.24763-1-pclouds@gmail.com","subject":"[PATCH v2 3/7] checkout: move 'confict_style' to checkout_opts","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-27T16:52:07Z","receivedAt":"2018-11-27T16:53:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 31245c1eb4..211a347a0c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -55,6 +55,7 @@ struct checkout_opts {\n \tint new_branch_log;\n \tenum branch_track track;\n \tstruct diff_options diff_options;\n+\tchar *conflict_style;\n \n \tint branch_exists;\n \tconst char *prefix;\n@@ -1228,7 +1229,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct checkout_opts real_opts;\n \tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n-\tchar *conflict_style = NULL;\n \tint dwim_new_local_branch = 1;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n@@ -1254,7 +1254,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n@@ -1290,9 +1290,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\topts->show_progress = isatty(2);\n \t}\n \n-\tif (conflict_style) {\n+\tif (opts->conflict_style) {\n \t\topts->merge = 1; /* implied */\n-\t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n+\t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n-- \n2.19.1.1327.g328c130451.dirty\n\n"},{"id":"364152","messageId":"CAGZ79kYXYzS6mHJWSkC-gAj_Ts2z8-jCX9tuTenKE+bxTgbtHw@mail.gmail.com","threadId":"49793","inReplyTo":"20181127165211.24763-2-pclouds@gmail.com","subject":"Re: [PATCH v2 1/7] parse-options: allow parse_options_concat(NULL, options)","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-11-27T19:43:57Z","receivedAt":"2018-11-27T19:44:12Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Tue, Nov 27, 2018 at 8:53 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> There is currently no caller that calls this function with \"a\" being\n> NULL. But it will be introduced shortly. It is used to construct the\n> option array from scratch, e.g.\n>\n>    struct parse_options opts = NULL;\n>    opts = parse_options_concat(opts, opts_1);\n>    opts = parse_options_concat(opts, opts_2);\n\nWhile this addresses the immediate needs, I'd prefer to think\nabout the API exposure of parse_options_concat,\n(related: do we want to have docs in its header file?)\nand I'd recommend to make it symmetrical, i.e.\nallow the second argument to also be NULL?\n\nIn the example given here, you'd just short it to\n\n    struct parse_options opts = opts_1;\n    opts = parse_options_concat(opts, opts_2);\n\nif not for this patch. Are opts_{1,2} ever be NULL?\n"},{"id":"364153","messageId":"CAGZ79ka5bYyz4nyzUFqwNzwDpkt6n6vCr-kEbG9xwJmcJyXsPg@mail.gmail.com","threadId":"49793","inReplyTo":"20181127165211.24763-4-pclouds@gmail.com","subject":"Re: [PATCH v2 3/7] checkout: move 'confict_style' to checkout_opts","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-11-27T19:50:50Z","receivedAt":"2018-11-27T19:51:05Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Tue, Nov 27, 2018 at 8:53 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n\nThe last patches seemed self explanatory after the first RFC\nand their commit message. This one is harder to reason about,\nas --conflict is documented as \"The same as --merge option\nabove, but ...\" and --merge is \"When switching branches, ...\"\nso ... ah my mind wandered off, expecting this to be a preparation\nfor the separate commands already, but this is just about ensuring\neverything is in opts such that the split can be done later?\n"},{"id":"364155","messageId":"CAGZ79kbKwYgs2un8phaQ9fMSpYqATGV+A-B0aH1gDKykrHBk8w@mail.gmail.com","threadId":"49793","inReplyTo":"20181127165211.24763-5-pclouds@gmail.com","subject":"Re: [PATCH v2 4/7] checkout: move dwim_new_local_branch to checkout_opts","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-11-27T19:52:28Z","receivedAt":"2018-11-27T19:52:44Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Tue, Nov 27, 2018 at 8:53 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nI would not mind to have this squashed into the previous patch\nbut keeping it separated is fine, too.\n(Reason for squashing: it makes it clearer that we do not\ncare about one specific option, but have to treat all the loose\noptions the same way.)\n"},{"id":"364189","messageId":"xmqqk1kxst9k.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"20181127165211.24763-2-pclouds@gmail.com","subject":"Re: [PATCH v2 1/7] parse-options: allow parse_options_concat(NULL, options)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-28T04:47:19Z","receivedAt":"2018-11-28T04:47:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> There is currently no caller that calls this function with \"a\" being\n> NULL. But it will be introduced shortly. It is used to construct the\n> option array from scratch, e.g.\n>\n>    struct parse_options opts = NULL;\n\nMissing asterisk somewhere?\n\n>    opts = parse_options_concat(opts, opts_1);\n>    opts = parse_options_concat(opts, opts_2);\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  parse-options-cb.c | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n>\n> diff --git a/parse-options-cb.c b/parse-options-cb.c\n> index 8c9edce52f..c609d52926 100644\n> --- a/parse-options-cb.c\n> +++ b/parse-options-cb.c\n> @@ -126,7 +126,7 @@ struct option *parse_options_concat(struct option *a, struct option *b)\n>  \tstruct option *ret;\n>  \tsize_t i, a_len = 0, b_len = 0;\n>  \n> -\tfor (i = 0; a[i].type != OPTION_END; i++)\n> +\tfor (i = 0; a && a[i].type != OPTION_END; i++)\n>  \t\ta_len++;\n>  \tfor (i = 0; b[i].type != OPTION_END; i++)\n>  \t\tb_len++;\n"},{"id":"364191","messageId":"xmqqftvlspqn.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"20181127165211.24763-7-pclouds@gmail.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-28T06:03:28Z","receivedAt":"2018-11-28T06:03:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> The good old \"git checkout\" command is still here and will be until\n> all (or most of users) are sick of it.\n\nTwo comments on the goal (the implementation looked reasonable\nassuming the reader agrees with the gaol).\n\nAt least to me, the verb \"switch\" needs two things to switch\nbetween, i.e. \"switch A and B\", unless it is \"switch to X\".\nEither \"switch-to-branch\" or simply \"switch-to\", perhaps?\n\nAs I already hinted in my response to Stefan (?) about\ncheckout-from-tree vs checkout-from-index, a command with multiple\nmodes of operation is not confusing to people with the right mental\nmodel, and I suspect that having two separate commands for \"checking\nout a branch\" and \"checking out paths\" that is done by this step\nwould help users to form the right mental model.  So I tend to think\nthese two are \"training wheels\", and suspect that once they got it,\nnobody will become \"sick of\" the single \"checkout\" command that can\nbe used to do either.  It's just the matter of being aware what can\nbe done (which requires the right mental model) and how to tell Git\nwhat the user wants it do (two separate commands, operating mode\noption, or just the implied command line syntax---once the user\nknows what s/he is doing, these do not make that much a difference).\n\nAs to the implementation,\n\n>  \tint dwim_new_local_branch;\n> +\tint accept_pathspec;\n> +\tint empty_arg_ok;\n\nI think this is a reasonable way to completely avoid the codepath\nthat considers an unknown arg being parsed could be a pathspec\nelement (e.g. switch-to-that-branch mode will never want to see\npathspec, so disambiguation logic and/or hint should not kick in).\n\nAt the beginning of cmd_switch_branches(), please \"explicitly\"\nassign 0 to accept_pathspec field, after memset(0) clears the whole\nstructure.  When a reader sees memset(0), it is read as \"giving a\nclean and bland slate to futz with with a reasonable default\", but\nfor this particular field, i.e. accept_pathspec, there is NO\nreasoanble default.  It's not like switch-to-branch mode is the heir\nto checkout and the other sibling checkout-files is doing a\nnon-default behaviour.\n\nSame comment probably would apply to the other one, although I\ndidn't think too deeply about that one.\n\n\n>  \t/*\n>  \t * If new checkout options are added, skip_merge_working_tree\n> @@ -1056,7 +1068,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n>  \targ = argv[0];\n>  \tdash_dash_pos = -1;\n>  \tfor (i = 0; i < argc; i++) {\n> -\t\tif (!strcmp(argv[i], \"--\")) {\n> +\t\tif (opts->accept_pathspec && !strcmp(argv[i], \"--\")) {\n>  \t\t\tdash_dash_pos = i;\n>  \t\t\tbreak;\n>  \t\t}\n> @@ -1067,6 +1079,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n>  \t\thas_dash_dash = 1; /* case (3) or (1) */\n>  \telse if (dash_dash_pos >= 2)\n>  \t\tdie(_(\"only one reference expected, %d given.\"), dash_dash_pos);\n> +\telse if (!opts->accept_pathspec)\n> +\t\thas_dash_dash = 1;\n>  \n>  \tif (!strcmp(arg, \"-\"))\n>  \t\targ = \"@{-1}\";\n> @@ -1291,30 +1305,23 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n>  \treturn newopts;\n>  }\n>  \n> -int cmd_checkout(int argc, const char **argv, const char *prefix)\n> +static int checkout_main(int argc, const char **argv, const char *prefix,\n> +\t\t\t struct checkout_opts *opts, struct option *options,\n> +\t\t\t const char * const usagestr[])\n>  {\n> -\tstruct checkout_opts real_opts;\n> -\tstruct checkout_opts *opts = &real_opts;\n>  \tstruct branch_info new_branch_info;\n>  \tint dwim_remotes_matched = 0;\n> -\tstruct option *options = NULL;\n>  \n> -\tmemset(opts, 0, sizeof(*opts));\n>  \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n>  \topts->overwrite_ignore = 1;\n>  \topts->prefix = prefix;\n>  \topts->show_progress = -1;\n> -\topts->dwim_new_local_branch = 1;\n>  \n>  \tgit_config(git_checkout_config, opts);\n>  \n>  \topts->track = BRANCH_TRACK_UNSPECIFIED;\n>  \n> -\toptions = add_common_options(opts, options);\n> -\toptions = add_switch_branch_options(opts, options);\n> -\toptions = add_checkout_path_options(opts, options);\n> -\n> -\targc = parse_options(argc, argv, prefix, options, checkout_usage,\n> +\targc = parse_options(argc, argv, prefix, options, usagestr,\n>  \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n>  \n>  \tif (opts->show_progress < 0) {\n> @@ -1381,7 +1388,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \t\t\t\t\t     &dwim_remotes_matched);\n>  \t\targv += n;\n>  \t\targc -= n;\n> -\t}\n> +\t} else if (!opts->empty_arg_ok)\n> +\t\tusage_with_options(usagestr, options);\n>  \n>  \tif (argc) {\n>  \t\tparse_pathspec(&opts->pathspec, 0,\n> @@ -1443,3 +1451,60 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \t\treturn checkout_branch(opts, &new_branch_info);\n>  \t}\n>  }\n> +\n> +int cmd_checkout(int argc, const char **argv, const char *prefix)\n> +{\n> +\tstruct checkout_opts opts;\n> +\tstruct option *options = NULL;\n> +\tint ret;\n> +\n> +\tmemset(&opts, 0, sizeof(opts));\n> +\topts.dwim_new_local_branch = 1;\n> +\topts.accept_pathspec = 1;\n> +\topts.empty_arg_ok = 1;\n> +\n> +\toptions = add_common_options(&opts, options);\n> +\toptions = add_switch_branch_options(&opts, options);\n> +\toptions = add_checkout_path_options(&opts, options);\n> +\n> +\tret = checkout_main(argc, argv, prefix, &opts,\n> +\t\t\t    options, checkout_usage);\n> +\tFREE_AND_NULL(options);\n> +\treturn ret;\n> +}\n> +\n> +int cmd_switch_branch(int argc, const char **argv, const char *prefix)\n> +{\n> +\tstruct checkout_opts opts;\n> +\tstruct option *options = NULL;\n> +\tint ret;\n> +\n> +\tmemset(&opts, 0, sizeof(opts));\n> +\topts.dwim_new_local_branch = 1;\n> +\n> +\toptions = add_common_options(&opts, options);\n> +\toptions = add_switch_branch_options(&opts, options);\n> +\n> +\tret = checkout_main(argc, argv, prefix, &opts,\n> +\t\t\t    options, switch_branch_usage);\n> +\tFREE_AND_NULL(options);\n> +\treturn ret;\n> +}\n> +\n> +int cmd_checkout_files(int argc, const char **argv, const char *prefix)\n> +{\n> +\tstruct checkout_opts opts;\n> +\tstruct option *options = NULL;\n> +\tint ret;\n> +\n> +\tmemset(&opts, 0, sizeof(opts));\n> +\topts.accept_pathspec = 1;\n> +\n> +\toptions = add_common_options(&opts, options);\n> +\toptions = add_checkout_path_options(&opts, options);\n> +\n> +\tret = checkout_main(argc, argv, prefix, &opts,\n> +\t\t\t    options, checkout_files_usage);\n> +\tFREE_AND_NULL(options);\n> +\treturn ret;\n> +}\n> diff --git a/git.c b/git.c\n> index 2f604a41ea..3b86ba765c 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -457,6 +457,7 @@ static struct cmd_struct commands[] = {\n>  \t{ \"check-mailmap\", cmd_check_mailmap, RUN_SETUP },\n>  \t{ \"check-ref-format\", cmd_check_ref_format, NO_PARSEOPT  },\n>  \t{ \"checkout\", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },\n> +\t{ \"checkout-files\", cmd_checkout_files, RUN_SETUP | NEED_WORK_TREE },\n>  \t{ \"checkout-index\", cmd_checkout_index,\n>  \t\tRUN_SETUP | NEED_WORK_TREE},\n>  \t{ \"cherry\", cmd_cherry, RUN_SETUP },\n> @@ -557,6 +558,7 @@ static struct cmd_struct commands[] = {\n>  \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n>  \t{ \"stripspace\", cmd_stripspace },\n>  \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n> +\t{ \"switch-branch\", cmd_switch_branch, RUN_SETUP | NEED_WORK_TREE },\n>  \t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n>  \t{ \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n>  \t{ \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\n"},{"id":"364192","messageId":"xmqqbm69spp4.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"20181127165211.24763-8-pclouds@gmail.com","subject":"Re: [PATCH v2 7/7] Suggest other commands instead of \"git checkout\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-28T06:04:23Z","receivedAt":"2018-11-28T06:04:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> The assumption made is here\n>\n> - \"git checkout\" is a horrible monster that should only be touched\n>   with a two-meter pole\n>\n> - there are other commands that can achieve the same thing\n\nThanks for clearly spelling out the assumptions.  It is good that\nthis step cames at the end, as the earlier 6 steps looked reasonable\nto me.\n\nThanks.\n\n\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  Documentation/git-branch.txt           |  8 ++--\n>  Documentation/git-check-ref-format.txt |  2 +-\n>  Documentation/git-format-patch.txt     |  2 +-\n>  Documentation/git-merge-base.txt       |  2 +-\n>  Documentation/git-rebase.txt           |  2 +-\n>  Documentation/git-remote.txt           |  2 +-\n>  Documentation/git-rerere.txt           | 10 ++---\n>  Documentation/git-reset.txt            | 18 ++++-----\n>  Documentation/git-revert.txt           |  2 +-\n>  Documentation/git-stash.txt            |  6 +--\n>  Documentation/gitattributes.txt        |  2 +-\n>  Documentation/gitcli.txt               |  4 +-\n>  Documentation/gitcore-tutorial.txt     | 18 ++++-----\n>  Documentation/giteveryday.txt          | 24 ++++++------\n>  Documentation/githooks.txt             |  5 ++-\n>  Documentation/gittutorial-2.txt        |  2 +-\n>  Documentation/gittutorial.txt          |  4 +-\n>  Documentation/revisions.txt            |  2 +-\n>  Documentation/user-manual.txt          | 54 +++++++++++++-------------\n>  advice.c                               |  2 +-\n>  sha1-name.c                            |  2 +-\n>  wt-status.c                            |  2 +-\n>  22 files changed, 88 insertions(+), 87 deletions(-)\n>\n> diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\n> index bf5316ffa9..1564df47d2 100644\n> --- a/Documentation/git-branch.txt\n> +++ b/Documentation/git-branch.txt\n> @@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>\n>  which points to the current `HEAD`, or <start-point> if given.\n>  \n>  Note that this will create the new branch, but it will not switch the\n> -working tree to it; use \"git checkout <newbranch>\" to switch to the\n> +working tree to it; use \"git switch-branch <newbranch>\" to switch to the\n>  new branch.\n>  \n>  When a local branch is started off a remote-tracking branch, Git sets up the\n> @@ -194,7 +194,7 @@ This option is only applicable in non-verbose mode.\n>  +\n>  This behavior is the default when the start point is a remote-tracking branch.\n>  Set the branch.autoSetupMerge configuration variable to `false` if you\n> -want `git checkout` and `git branch` to always behave as if `--no-track`\n> +want `git switch-branch` and `git branch` to always behave as if `--no-track`\n>  were given. Set it to `always` if you want this behavior when the\n>  start-point is either a local or remote-tracking branch.\n>  \n> @@ -293,7 +293,7 @@ Start development from a known tag::\n>  $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6\n>  $ cd my2.6\n>  $ git branch my2.6.14 v2.6.14   <1>\n> -$ git checkout my2.6.14\n> +$ git switch-branch my2.6.14\n>  ------------\n>  +\n>  <1> This step and the next one could be combined into a single step with\n> @@ -319,7 +319,7 @@ NOTES\n>  -----\n>  \n>  If you are creating a branch that you want to checkout immediately, it is\n> -easier to use the git checkout command with its `-b` option to create\n> +easier to use the \"git switch-branch\" command with its `-b` option to create\n>  a branch and check it out with a single command.\n>  \n>  The options `--contains`, `--no-contains`, `--merged` and `--no-merged`\n> diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt\n> index d9de992585..38c2169d7a 100644\n> --- a/Documentation/git-check-ref-format.txt\n> +++ b/Documentation/git-check-ref-format.txt\n> @@ -88,7 +88,7 @@ but it is explicitly forbidden at the beginning of a branch name).\n>  When run with `--branch` option in a repository, the input is first\n>  expanded for the ``previous checkout syntax''\n>  `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that\n> -was checked out using \"git checkout\" operation. This option should be\n> +was checked out using \"git switch-branch\" operation. This option should be\n>  used by porcelains to accept this syntax anywhere a branch name is\n>  expected, so they can act as if you typed the branch name. As an\n>  exception note that, the ``previous checkout operation'' might result\n> diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\n> index aba4c5febe..0ceaa1173c 100644\n> --- a/Documentation/git-format-patch.txt\n> +++ b/Documentation/git-format-patch.txt\n> @@ -416,7 +416,7 @@ One way to test if your MUA is set up correctly is:\n>  * Apply it:\n>  \n>      $ git fetch <project> master:test-apply\n> -    $ git checkout test-apply\n> +    $ git switch-branch test-apply\n>      $ git reset --hard\n>      $ git am a.patch\n>  \n> diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt\n> index 9f07f4f6ed..1b25e5d530 100644\n> --- a/Documentation/git-merge-base.txt\n> +++ b/Documentation/git-merge-base.txt\n> @@ -149,7 +149,7 @@ instead.\n>  Discussion on fork-point mode\n>  -----------------------------\n>  \n> -After working on the `topic` branch created with `git checkout -b\n> +After working on the `topic` branch created with `git switch-branch -b\n>  topic origin/master`, the history of remote-tracking branch\n>  `origin/master` may have been rewound and rebuilt, leading to a\n>  history of this shape:\n> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\n> index 80793bad8d..fe10880633 100644\n> --- a/Documentation/git-rebase.txt\n> +++ b/Documentation/git-rebase.txt\n> @@ -17,7 +17,7 @@ SYNOPSIS\n>  DESCRIPTION\n>  -----------\n>  If <branch> is specified, 'git rebase' will perform an automatic\n> -`git checkout <branch>` before doing anything else.  Otherwise\n> +`git switch-branch <branch>` before doing anything else.  Otherwise\n>  it remains on the current branch.\n>  \n>  If <upstream> is not specified, the upstream configured in\n> diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\n> index 0cad37fb81..044bbdb27c 100644\n> --- a/Documentation/git-remote.txt\n> +++ b/Documentation/git-remote.txt\n> @@ -230,7 +230,7 @@ $ git branch -r\n>    staging/master\n>    staging/staging-linus\n>    staging/staging-next\n> -$ git checkout -b staging staging/master\n> +$ git switch-branch -b staging staging/master\n>  ...\n>  ------------\n>  \n> diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt\n> index df310d2a58..fe9d21b395 100644\n> --- a/Documentation/git-rerere.txt\n> +++ b/Documentation/git-rerere.txt\n> @@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.\n>  One way to do it is to pull master into the topic branch:\n>  \n>  ------------\n> -\t$ git checkout topic\n> +\t$ git switch-branch topic\n>  \t$ git merge master\n>  \n>                o---*---o---+ topic\n> @@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,\n>  in which case the final commit graph would look like this:\n>  \n>  ------------\n> -\t$ git checkout topic\n> +\t$ git switch-branch topic\n>  \t$ git merge master\n>  \t$ ... work on both topic and master branches\n> -\t$ git checkout master\n> +\t$ git switch-branch master\n>  \t$ git merge topic\n>  \n>                o---*---o---+---o---o topic\n> @@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on\n>  top of the tip before the test merge:\n>  \n>  ------------\n> -\t$ git checkout topic\n> +\t$ git switch-branch topic\n>  \t$ git merge master\n>  \t$ git reset --hard HEAD^ ;# rewind the test merge\n>  \t$ ... work on both topic and master branches\n> -\t$ git checkout master\n> +\t$ git switch-branch master\n>  \t$ git merge topic\n>  \n>                o---*---o-------o---o topic\n> diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\n> index 2dac95c71a..ca46b4c967 100644\n> --- a/Documentation/git-reset.txt\n> +++ b/Documentation/git-reset.txt\n> @@ -149,9 +149,9 @@ See also the --amend option to linkgit:git-commit[1].\n>  Undo a commit, making it a topic branch::\n>  +\n>  ------------\n> -$ git branch topic/wip     <1>\n> -$ git reset --hard HEAD~3  <2>\n> -$ git checkout topic/wip   <3>\n> +$ git branch topic/wip          <1>\n> +$ git reset --hard HEAD~3       <2>\n> +$ git switch-branch topic/wip   <3>\n>  ------------\n>  +\n>  <1> You have made some commits, but realize they were premature\n> @@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you\n>  need to get to the other branch for a quick bugfix.\n>  +\n>  ------------\n> -$ git checkout feature ;# you were working in \"feature\" branch and\n> +$ git switch-branch feature ;# you were working in \"feature\" branch and\n>  $ work work work       ;# got interrupted\n>  $ git commit -a -m \"snapshot WIP\"                 <1>\n> -$ git checkout master\n> +$ git switch-branch master\n>  $ fix fix fix\n>  $ git commit ;# commit with real log\n> -$ git checkout feature\n> +$ git switch-branch feature\n>  $ git reset --soft HEAD^ ;# go back to WIP state  <2>\n>  $ git reset                                       <3>\n>  ------------\n> @@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.\n>  +\n>  ------------\n>  $ git tag start\n> -$ git checkout -b branch1\n> +$ git switch-branch -b branch1\n>  $ edit\n>  $ git commit ...                            <1>\n>  $ edit\n> -$ git checkout -b branch2                   <2>\n> +$ git switch-branch -b branch2              <2>\n>  $ git reset --keep start                    <3>\n>  ------------\n>  +\n>  <1> This commits your first edits in branch1.\n>  <2> In the ideal world, you could have realized that the earlier\n>      commit did not belong to the new topic when you created and switched\n> -    to branch2 (i.e. \"git checkout -b branch2 start\"), but nobody is\n> +    to branch2 (i.e. \"git switch-branch -b branch2 start\"), but nobody is\n>      perfect.\n>  <3> But you can use \"reset --keep\" to remove the unwanted commit after\n>      you switched to \"branch2\".\n> diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\n> index 837707a8fd..e49dbbec83 100644\n> --- a/Documentation/git-revert.txt\n> +++ b/Documentation/git-revert.txt\n> @@ -26,7 +26,7 @@ effect of some earlier commits (often only a faulty one).  If you want to\n>  throw away all uncommitted changes in your working directory, you\n>  should see linkgit:git-reset[1], particularly the `--hard` option.  If\n>  you want to extract specific files as they were in another commit, you\n> -should see linkgit:git-checkout[1], specifically the `git checkout\n> +should see linkgit:git-checkout[1], specifically the `git checkout-files\n>  <commit> -- <filename>` syntax.  Take care with these alternatives as\n>  both will discard uncommitted changes in your working directory.\n>  \n> diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\n> index 7ef8c47911..ea226979b1 100644\n> --- a/Documentation/git-stash.txt\n> +++ b/Documentation/git-stash.txt\n> @@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:\n>  +\n>  ----------------------------------------------------------------\n>  # ... hack hack hack ...\n> -$ git checkout -b my_wip\n> +$ git switch-branch -b my_wip\n>  $ git commit -a -m \"WIP\"\n> -$ git checkout master\n> +$ git switch-branch master\n>  $ edit emergency fix\n>  $ git commit -a -m \"Fix in a hurry\"\n> -$ git checkout my_wip\n> +$ git switch-branch my_wip\n>  $ git reset --soft HEAD^\n>  # ... continue hacking ...\n>  ----------------------------------------------------------------\n> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n> index b8392fc330..df62bd8019 100644\n> --- a/Documentation/gitattributes.txt\n> +++ b/Documentation/gitattributes.txt\n> @@ -112,7 +112,7 @@ Checking-out and checking-in\n>  \n>  These attributes affect how the contents stored in the\n>  repository are copied to the working tree files when commands\n> -such as 'git checkout' and 'git merge' run.  They also affect how\n> +such as 'git switch-branch' and 'git merge' run.  They also affect how\n>  Git stores the contents you prepare in the working tree in the\n>  repository upon 'git add' and 'git commit'.\n>  \n> diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt\n> index 592e06d839..0ad4869f2c 100644\n> --- a/Documentation/gitcli.txt\n> +++ b/Documentation/gitcli.txt\n> @@ -47,8 +47,8 @@ disambiguating `--` at appropriate places.\n>     things:\n>  +\n>  --------------------------------\n> -$ git checkout -- *.c\n> -$ git checkout -- \\*.c\n> +$ git checkout-files -- *.c\n> +$ git checkout-files -- \\*.c\n>  --------------------------------\n>  +\n>  The former lets your shell expand the fileglob, and you are asking\n> diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt\n> index e29a9effcc..49a8b5aa52 100644\n> --- a/Documentation/gitcore-tutorial.txt\n> +++ b/Documentation/gitcore-tutorial.txt\n> @@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just\n>  saying that you want to check out a new branch:\n>  \n>  ------------\n> -$ git checkout -b mybranch\n> +$ git branch mybranch\n>  ------------\n>  \n>  will create a new branch based at the current `HEAD` position, and switch\n> @@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.\n>  In other words, if you have an earlier tag or branch, you'd just do\n>  \n>  ------------\n> -$ git checkout -b mybranch earlier-commit\n> +$ git switch-branch -b mybranch earlier-commit\n>  ------------\n>  \n>  and it would create the new branch `mybranch` at the earlier commit,\n> @@ -765,7 +765,7 @@ and check out the state at that time.\n>  You can always just jump back to your original `master` branch by doing\n>  \n>  ------------\n> -$ git checkout master\n> +$ git switch-branch master\n>  ------------\n>  \n>  (or any other branch-name, for that matter) and if you forget which\n> @@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]\n>  \n>  which will simply _create_ the branch, but will not do anything further.\n>  You can then later -- once you decide that you want to actually develop\n> -on that branch -- switch to that branch with a regular 'git checkout'\n> +on that branch -- switch to that branch with a regular 'git switch-branch\n>  with the branchname as the argument.\n>  \n>  \n> @@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in\n>  that branch, and do some work there.\n>  \n>  ------------------------------------------------\n> -$ git checkout mybranch\n> +$ git switch-branch mybranch\n>  $ echo \"Work, work, work\" >>hello\n>  $ git commit -m \"Some work.\" -i hello\n>  ------------------------------------------------\n> @@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back\n>  to the master branch, and editing the same file differently there:\n>  \n>  ------------\n> -$ git checkout master\n> +$ git switch-branch master\n>  ------------\n>  \n>  Here, take a moment to look at the contents of `hello`, and notice how they\n> @@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run\n>  'git merge' to get the \"upstream changes\" back to your branch.\n>  \n>  ------------\n> -$ git checkout mybranch\n> +$ git switch-branch mybranch\n>  $ git merge -m \"Merge upstream changes.\" master\n>  ------------\n>  \n> @@ -1133,9 +1133,9 @@ Remember, before running 'git merge', our `master` head was at\n>  work.\" commit.\n>  \n>  ------------\n> -$ git checkout mybranch\n> +$ git switch-branch mybranch\n>  $ git reset --hard master^2\n> -$ git checkout master\n> +$ git switch-branch master\n>  $ git reset --hard master^\n>  ------------\n>  \n> diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\n> index 9f2528fc8c..861b2bb616 100644\n> --- a/Documentation/giteveryday.txt\n> +++ b/Documentation/giteveryday.txt\n> @@ -80,9 +80,9 @@ $ git tag v2.43 <2>\n>  Create a topic branch and develop.::\n>  +\n>  ------------\n> -$ git checkout -b alsa-audio <1>\n> +$ git branch alsa-audio <1>\n>  $ edit/compile/test\n> -$ git checkout -- curses/ux_audio_oss.c <2>\n> +$ git checkout-files -- curses/ux_audio_oss.c <2>\n>  $ git add curses/ux_audio_alsa.c <3>\n>  $ edit/compile/test\n>  $ git diff HEAD <4>\n> @@ -90,7 +90,7 @@ $ git commit -a -s <5>\n>  $ edit/compile/test\n>  $ git diff HEAD^ <6>\n>  $ git commit -a --amend <7>\n> -$ git checkout master <8>\n> +$ git switch-branch master <8>\n>  $ git merge alsa-audio <9>\n>  $ git log --since='3 days ago' <10>\n>  $ git log v2.43.. curses/ <11>\n> @@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::\n>  ------------\n>  $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6\n>  $ cd my2.6\n> -$ git checkout -b mine master <1>\n> +$ git switch-branch -b mine master <1>\n>  $ edit/compile/test; git commit -a -s <2>\n>  $ git format-patch master <3>\n>  $ git send-email --to=\"person <email@example.com>\" 00*.patch <4>\n> -$ git checkout master <5>\n> +$ git switch-branch master <5>\n>  $ git pull <6>\n>  $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>\n>  $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>\n> @@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit\n>  satellite$ git push origin <4>\n>  \n>  mothership$ cd frotz\n> -mothership$ git checkout master\n> +mothership$ git switch-branch master\n>  mothership$ git merge satellite/master <5>\n>  ------------\n>  +\n> @@ -216,7 +216,7 @@ machine into the master branch.\n>  Branch off of a specific tag.::\n>  +\n>  ------------\n> -$ git checkout -b private2.6.14 v2.6.14 <1>\n> +$ git switch-branch -b private2.6.14 v2.6.14 <1>\n>  $ edit/compile/test; git commit -a\n>  $ git checkout master\n>  $ git cherry-pick v2.6.14..private2.6.14 <2>\n> @@ -274,14 +274,14 @@ $ mailx <3>\n>  & s 2 3 4 5 ./+to-apply\n>  & s 7 8 ./+hold-linus\n>  & q\n> -$ git checkout -b topic/one master\n> +$ git switch-branch -b topic/one master\n>  $ git am -3 -i -s ./+to-apply <4>\n>  $ compile/test\n> -$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>\n> -$ git checkout topic/one && git rebase master <6>\n> -$ git checkout pu && git reset --hard next <7>\n> +$ git switch-branch -b hold/linus && git am -3 -i -s ./+hold-linus <5>\n> +$ git switch-branch topic/one && git rebase master <6>\n> +$ git switch-branch pu && git reset --hard next <7>\n>  $ git merge topic/one topic/two && git merge hold/linus <8>\n> -$ git checkout maint\n> +$ git switch-branch maint\n>  $ git cherry-pick master~4 <9>\n>  $ compile/test\n>  $ git tag -s -m \"GIT 0.99.9x\" v0.99.9x <10>\n> diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\n> index 959044347e..3939ec774a 100644\n> --- a/Documentation/githooks.txt\n> +++ b/Documentation/githooks.txt\n> @@ -166,7 +166,7 @@ worktree.  The hook is given three parameters: the ref of the previous HEAD,\n>  the ref of the new HEAD (which may or may not have changed), and a flag\n>  indicating whether the checkout was a branch checkout (changing branches,\n>  flag=1) or a file checkout (retrieving a file from the index, flag=0).\n> -This hook cannot affect the outcome of `git checkout`.\n> +This hook cannot affect the outcome of `git switch-branch` or `git checkout`.\n>  \n>  It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is\n>  used. The first parameter given to the hook is the null-ref, the second the\n> @@ -402,7 +402,8 @@ exit with a zero status.\n>  For example, the hook can simply run `git read-tree -u -m HEAD \"$1\"`\n>  in order to emulate `git fetch` that is run in the reverse direction\n>  with `git push`, as the two-tree form of `git read-tree -u -m` is\n> -essentially the same as `git checkout` that switches branches while\n> +essentially the same as `git switch-branch` or `git checkout`\n> +that switches branches while\n>  keeping the local changes in the working tree that do not interfere\n>  with the difference between the branches.\n>  \n> diff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt\n> index e0976f6017..125213d951 100644\n> --- a/Documentation/gittutorial-2.txt\n> +++ b/Documentation/gittutorial-2.txt\n> @@ -376,7 +376,7 @@ Changes to be committed:\n>  \n>  Changes not staged for commit:\n>    (use \"git add <file>...\" to update what will be committed)\n> -  (use \"git checkout -- <file>...\" to discard changes in working directory)\n> +  (use \"git checkout-files -- <file>...\" to discard changes in working directory)\n>  \n>  \tmodified:   file.txt\n>  \n> diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\n> index 242de31cb6..396e55c191 100644\n> --- a/Documentation/gittutorial.txt\n> +++ b/Documentation/gittutorial.txt\n> @@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;\n>  type\n>  \n>  ------------------------------------------------\n> -$ git checkout experimental\n> +$ git switch-branch experimental\n>  ------------------------------------------------\n>  \n>  to switch to the experimental branch.  Now edit a file, commit the\n> @@ -216,7 +216,7 @@ change, and switch back to the master branch:\n>  ------------------------------------------------\n>  (edit file)\n>  $ git commit -a\n> -$ git checkout master\n> +$ git switch-branch master\n>  ------------------------------------------------\n>  \n>  Check that the change you made is no longer visible, since it was\n> diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\n> index 72daa20e76..f55502cd50 100644\n> --- a/Documentation/revisions.txt\n> +++ b/Documentation/revisions.txt\n> @@ -115,7 +115,7 @@ Here's an example to make it more clear:\n>  ------------------------------\n>  $ git config push.default current\n>  $ git config remote.pushdefault myfork\n> -$ git checkout -b mybranch origin/master\n> +$ git switch-branch -b mybranch origin/master\n>  \n>  $ git rev-parse --symbolic-full-name @{upstream}\n>  refs/remotes/origin/master\n> diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\n> index eff7890274..56397e93d4 100644\n> --- a/Documentation/user-manual.txt\n> +++ b/Documentation/user-manual.txt\n> @@ -125,7 +125,7 @@ Create a new branch head pointing to one of these versions and check it\n>  out using linkgit:git-checkout[1]:\n>  \n>  ------------------------------------------------\n> -$ git checkout -b new v2.6.13\n> +$ git switch-branch -b new v2.6.13\n>  ------------------------------------------------\n>  \n>  The working directory then reflects the contents that the project had\n> @@ -282,10 +282,10 @@ a summary of the commands:\n>  \tthis command will fail with a warning.\n>  `git branch -D <branch>`::\n>  \tdelete the branch `<branch>` irrespective of its merged status.\n> -`git checkout <branch>`::\n> +`git switch-branch <branch>`::\n>  \tmake the current branch `<branch>`, updating the working\n>  \tdirectory to reflect the version referenced by `<branch>`.\n> -`git checkout -b <new> <start-point>`::\n> +`git switch-branch -b <new> <start-point>`::\n>  \tcreate a new branch `<new>` referencing `<start-point>`, and\n>  \tcheck it out.\n>  \n> @@ -302,12 +302,12 @@ ref: refs/heads/master\n>  Examining an old version without creating a new branch\n>  ------------------------------------------------------\n>  \n> -The `git checkout` command normally expects a branch head, but will also\n> +The `git switch-branch` command normally expects a branch head, but will also\n>  accept an arbitrary commit; for example, you can check out the commit\n>  referenced by a tag:\n>  \n>  ------------------------------------------------\n> -$ git checkout v2.6.17\n> +$ git switch-branch v2.6.17\n>  Note: checking out 'v2.6.17'.\n>  \n>  You are in 'detached HEAD' state. You can look around, make experimental\n> @@ -317,7 +317,7 @@ state without impacting any branches by performing another checkout.\n>  If you want to create a new branch to retain commits you create, you may\n>  do so (now or later) by using -b with the checkout command again. Example:\n>  \n> -  git checkout -b new_branch_name\n> +  git switch-branch -b new_branch_name\n>  \n>  HEAD is now at 427abfa Linux v2.6.17\n>  ------------------------------------------------\n> @@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches\n>  on a branch of your own, just as you would for a tag:\n>  \n>  ------------------------------------------------\n> -$ git checkout -b my-todo-copy origin/todo\n> +$ git switch-branch -b my-todo-copy origin/todo\n>  ------------------------------------------------\n>  \n>  You can also check out `origin/todo` directly to examine it or\n> @@ -1523,12 +1523,12 @@ Checking out an old version of a file\n>  \n>  In the process of undoing a previous bad change, you may find it\n>  useful to check out an older version of a particular file using\n> -linkgit:git-checkout[1].  We've used `git checkout` before to switch\n> +linkgit:git-checkout[1].  We've used `git switch-branch` before to switch\n>  branches, but it has quite different behavior if it is given a path\n>  name: the command\n>  \n>  -------------------------------------------------\n> -$ git checkout HEAD^ path/to/file\n> +$ git checkout-files HEAD^ path/to/file\n>  -------------------------------------------------\n>  \n>  replaces path/to/file by the contents it had in the commit HEAD^, and\n> @@ -2211,8 +2211,8 @@ $ git branch --track release origin/master\n>  These can be easily kept up to date using linkgit:git-pull[1].\n>  \n>  -------------------------------------------------\n> -$ git checkout test && git pull\n> -$ git checkout release && git pull\n> +$ git switch-branch test && git pull\n> +$ git switch-branch release && git pull\n>  -------------------------------------------------\n>  \n>  Important note!  If you have any local changes in these branches, then\n> @@ -2264,7 +2264,7 @@ tested changes\n>  2) help future bug hunters that use `git bisect` to find problems\n>  \n>  -------------------------------------------------\n> -$ git checkout -b speed-up-spinlocks v2.6.35\n> +$ git switch-branch -b speed-up-spinlocks v2.6.35\n>  -------------------------------------------------\n>  \n>  Now you apply the patch(es), run some tests, and commit the change(s).  If\n> @@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the\n>  \"test\" branch in preparation to make it public:\n>  \n>  -------------------------------------------------\n> -$ git checkout test && git merge speed-up-spinlocks\n> +$ git switch-branch test && git merge speed-up-spinlocks\n>  -------------------------------------------------\n>  \n>  It is unlikely that you would have any conflicts here ... but you might if you\n> @@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It\n>  means that the patches can be moved into the `release` tree in any order.\n>  \n>  -------------------------------------------------\n> -$ git checkout release && git merge speed-up-spinlocks\n> +$ git switch-branch release && git merge speed-up-spinlocks\n>  -------------------------------------------------\n>  \n>  After a while, you will have a number of branches, and despite the\n> @@ -2358,7 +2358,7 @@ Here are some of the scripts that simplify all this even further.\n>  \n>  case \"$1\" in\n>  test|release)\n> -\tgit checkout $1 && git pull . origin\n> +\tgit switch-branch $1 && git pull . origin\n>  \t;;\n>  origin)\n>  \tbefore=$(git rev-parse refs/remotes/origin/master)\n> @@ -2400,7 +2400,7 @@ test|release)\n>  \t\techo $1 already merged into $2 1>&2\n>  \t\texit 1\n>  \tfi\n> -\tgit checkout $2 && git pull . $1\n> +\tgit switch-branch $2 && git pull . $1\n>  \t;;\n>  *)\n>  \tusage\n> @@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch\n>  `origin`, and create some commits on top of it:\n>  \n>  -------------------------------------------------\n> -$ git checkout -b mywork origin\n> +$ git switch-branch -b mywork origin\n>  $ vi file.txt\n>  $ git commit\n>  $ vi otherfile.txt\n> @@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use\n>  linkgit:git-rebase[1]:\n>  \n>  -------------------------------------------------\n> -$ git checkout mywork\n> +$ git switch-branch mywork\n>  $ git rebase origin\n>  -------------------------------------------------\n>  \n> @@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the\n>  new commit:\n>  \n>  -------------------------------------------------\n> -$ git checkout master\n> +$ git switch-branch master\n>  -------------------------------------------------\n>  \n>  or\n>  \n>  -------------------------------------------------\n> -$ git checkout -b fix-up\n> +$ git switch-branch -b fix-up\n>  -------------------------------------------------\n>  \n>  then\n> @@ -4194,7 +4194,7 @@ start.\n>  A good place to start is with the contents of the initial commit, with:\n>  \n>  ----------------------------------------------------\n> -$ git checkout e83c5163\n> +$ git switch-branch e83c5163\n>  ----------------------------------------------------\n>  \n>  The initial revision lays the foundation for almost everything Git has\n> @@ -4437,10 +4437,10 @@ Managing branches\n>  -----------------\n>  \n>  -----------------------------------------------\n> -$ git branch\t     # list all local branches in this repo\n> -$ git checkout test  # switch working directory to branch \"test\"\n> -$ git branch new     # create branch \"new\" starting at current HEAD\n> -$ git branch -d new  # delete branch \"new\"\n> +$ git branch\t\t\t# list all local branches in this repo\n> +$ git switch-branch test\t# switch working directory to branch \"test\"\n> +$ git branch new\t\t# create branch \"new\" starting at current HEAD\n> +$ git branch -d new\t\t# delete branch \"new\"\n>  -----------------------------------------------\n>  \n>  Instead of basing a new branch on current HEAD (the default), use:\n> @@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch \"test\"\n>  Create and switch to a new branch at the same time:\n>  \n>  -----------------------------------------------\n> -$ git checkout -b new v2.6.15\n> +$ git switch-branch -b new v2.6.15\n>  -----------------------------------------------\n>  \n>  Update and examine branches from the repository you cloned from:\n> @@ -4467,7 +4467,7 @@ $ git branch -r\t\t# list\n>    origin/master\n>    origin/next\n>    ...\n> -$ git checkout -b masterwork origin/master\n> +$ git switch-branch -b masterwork origin/master\n>  -----------------------------------------------\n>  \n>  Fetch a branch from a different repository, and give it a new\n> diff --git a/advice.c b/advice.c\n> index 5f35656409..1befdb2163 100644\n> --- a/advice.c\n> +++ b/advice.c\n> @@ -195,7 +195,7 @@ void detach_advice(const char *new_name)\n>  \t\"state without impacting any branches by performing another checkout.\\n\\n\"\n>  \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n>  \t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n> -\t\"  git checkout -b <new-branch-name>\\n\\n\");\n> +\t\"  git branch <new-branch-name>\\n\\n\");\n>  \n>  \tfprintf(stderr, fmt, new_name);\n>  }\n> diff --git a/sha1-name.c b/sha1-name.c\n> index faa60f69e3..4e4e14a45c 100644\n> --- a/sha1-name.c\n> +++ b/sha1-name.c\n> @@ -771,7 +771,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,\n>  \t\"because it will be ignored when you just specify 40-hex. These refs\\n\"\n>  \t\"may be created by mistake. For example,\\n\"\n>  \t\"\\n\"\n> -\t\"  git checkout -b $br $(git rev-parse ...)\\n\"\n> +\t\"  git switch-branch -b $br $(git rev-parse ...)\\n\"\n>  \t\"\\n\"\n>  \t\"where \\\"$br\\\" is somehow empty and a 40-hex ref is created. Please\\n\"\n>  \t\"examine these refs and maybe delete them. Turn this message off by\\n\"\n> diff --git a/wt-status.c b/wt-status.c\n> index a24711374c..6266683926 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -224,7 +224,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s,\n>  \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git add <file>...\\\" to update what will be committed)\"));\n>  \telse\n>  \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git add/rm <file>...\\\" to update what will be committed)\"));\n> -\tstatus_printf_ln(s, c, _(\"  (use \\\"git checkout -- <file>...\\\" to discard changes in working directory)\"));\n> +\tstatus_printf_ln(s, c, _(\"  (use \\\"git checkout-files <file>...\\\" to discard changes in working directory)\"));\n>  \tif (has_dirty_submodules)\n>  \t\tstatus_printf_ln(s, c, _(\"  (commit or discard the untracked or modified content in submodules)\"));\n>  \tstatus_printf_ln(s, c, \"%s\", \"\");\n"},{"id":"364225","messageId":"CACsJy8BMkQZ=VEGCwKb2q==eqovMnGy+szitpAkn6LTQaFq+=A@mail.gmail.com","threadId":"49793","inReplyTo":"CAGZ79kYXYzS6mHJWSkC-gAj_Ts2z8-jCX9tuTenKE+bxTgbtHw@mail.gmail.com","subject":"Re: [PATCH v2 1/7] parse-options: allow parse_options_concat(NULL, options)","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-28T15:22:40Z","receivedAt":"2018-11-28T15:23:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Nov 27, 2018 at 8:44 PM Stefan Beller <sbeller@google.com> wrote:\n>\n> On Tue, Nov 27, 2018 at 8:53 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> >\n> > There is currently no caller that calls this function with \"a\" being\n> > NULL. But it will be introduced shortly. It is used to construct the\n> > option array from scratch, e.g.\n> >\n> >    struct parse_options opts = NULL;\n> >    opts = parse_options_concat(opts, opts_1);\n> >    opts = parse_options_concat(opts, opts_2);\n>\n> While this addresses the immediate needs, I'd prefer to think\n> about the API exposure of parse_options_concat,\n> (related: do we want to have docs in its header file?)\n> and I'd recommend to make it symmetrical, i.e.\n> allow the second argument to also be NULL?\n\nI'll just drop this patch. There's a better way to do the same without\nadding special handling like this.\n-- \nDuy\n"},{"id":"364226","messageId":"CACsJy8Bzs=FYKrR6h1cqVH32eEt2t8rUMtE2yFNvt+W55u=sDA@mail.gmail.com","threadId":"49793","inReplyTo":"xmqqftvlspqn.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-28T15:30:34Z","receivedAt":"2018-11-28T15:31:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Nov 28, 2018 at 7:03 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n> > The good old \"git checkout\" command is still here and will be until\n> > all (or most of users) are sick of it.\n>\n> Two comments on the goal (the implementation looked reasonable\n> assuming the reader agrees with the gaol).\n>\n> At least to me, the verb \"switch\" needs two things to switch\n> between, i.e. \"switch A and B\", unless it is \"switch to X\".\n> Either \"switch-to-branch\" or simply \"switch-to\", perhaps?\n>\n> As I already hinted in my response to Stefan (?) about\n> checkout-from-tree vs checkout-from-index, a command with multiple\n> modes of operation is not confusing to people with the right mental\n> model, and I suspect that having two separate commands for \"checking\n> out a branch\" and \"checking out paths\" that is done by this step\n> would help users to form the right mental model.\n\nSince the other one is already \"checkout-files\", maybe this one could\njust be \"checkout-branch\".\n\n> So I tend to think\n> these two are \"training wheels\", and suspect that once they got it,\n> nobody will become \"sick of\" the single \"checkout\" command that can\n> be used to do either.  It's just the matter of being aware what can\n> be done (which requires the right mental model) and how to tell Git\n> what the user wants it do (two separate commands, operating mode\n> option, or just the implied command line syntax---once the user\n> knows what s/he is doing, these do not make that much a difference).\n\nI would hope this becomes better defaults and being used 90% of time.\nEven though I know \"git checkout\" quite well, it still bites me from\ntime to time. Having the right mental model is one thing. Having to\nthink a bit every time to write \"git checkout\" with the right syntax,\nand whether you need \"--\" (that ambiguation problem can still bite you\nfrom time to time), is frankly something I'd rather avoid.\n-- \nDuy\n"},{"id":"364227","messageId":"CACsJy8BBeRDY85rJLzeJRFSazEn1rZ8JGN6_vwAr2ia=FaiLNg@mail.gmail.com","threadId":"49793","inReplyTo":"xmqqbm69spp4.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 7/7] Suggest other commands instead of \"git checkout\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-28T15:33:39Z","receivedAt":"2018-11-28T15:34:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Nov 28, 2018 at 7:04 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n> > The assumption made is here\n> >\n> > - \"git checkout\" is a horrible monster that should only be touched\n> >   with a two-meter pole\n> >\n> > - there are other commands that can achieve the same thing\n>\n> Thanks for clearly spelling out the assumptions.  It is good that\n> this step cames at the end, as the earlier 6 steps looked reasonable\n> to me.\n\nI see my deliberate attempt to provoke has failed :D Giving your view\nof the new commands as \"training wheels\", I take it we still should\nmake them visible as much as possible, but we just not try to hide\n\"git checkout\" as much (e.g. we mention both new and old commands,\ninstead of just mentioning the new one, when suggesting something)?\n-- \nDuy\n"},{"id":"364237","messageId":"CAGZ79kbWqfMHZeYFXNh00N5xSSkW0_Mzja1EtuzxQxrhESoZxQ@mail.gmail.com","threadId":"49793","inReplyTo":"CACsJy8Bzs=FYKrR6h1cqVH32eEt2t8rUMtE2yFNvt+W55u=sDA@mail.gmail.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-11-28T19:08:04Z","receivedAt":"2018-11-28T19:08:19Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Wed, Nov 28, 2018 at 7:31 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Wed, Nov 28, 2018 at 7:03 AM Junio C Hamano <gitster@pobox.com> wrote:\n> >\n> > Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n> >\n> > > The good old \"git checkout\" command is still here and will be until\n> > > all (or most of users) are sick of it.\n> >\n> > Two comments on the goal (the implementation looked reasonable\n> > assuming the reader agrees with the gaol).\n> >\n> > At least to me, the verb \"switch\" needs two things to switch\n> > between, i.e. \"switch A and B\", unless it is \"switch to X\".\n> > Either \"switch-to-branch\" or simply \"switch-to\", perhaps?\n> >\n> > As I already hinted in my response to Stefan (?) about\n> > checkout-from-tree vs checkout-from-index, a command with multiple\n> > modes of operation is not confusing to people with the right mental\n> > model, and I suspect that having two separate commands for \"checking\n> > out a branch\" and \"checking out paths\" that is done by this step\n> > would help users to form the right mental model.\n>\n> Since the other one is already \"checkout-files\", maybe this one could\n> just be \"checkout-branch\".\n\nI dislike the checkout-* names, as we already have checkout-index\nas plumbing, so it would be confusing as to which checkout-* command\nshould be used when and why as it seems the co-index moves\ncontent *from index* to the working tree, but the co-files moves content\n*to files*, whereas checkout-branch is neither 'moving' to or from a branch\nbut rather 'switching' to that branch.\n"},{"id":"364238","messageId":"CACsJy8AUogchEBhgv5mqUnr7drHwFdRFSNzwLoqrw7RSeNBgDQ@mail.gmail.com","threadId":"49793","inReplyTo":"CAGZ79kbWqfMHZeYFXNh00N5xSSkW0_Mzja1EtuzxQxrhESoZxQ@mail.gmail.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-28T19:18:06Z","receivedAt":"2018-11-28T19:18:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Nov 28, 2018 at 8:08 PM Stefan Beller <sbeller@google.com> wrote:\n>\n> On Wed, Nov 28, 2018 at 7:31 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> >\n> > On Wed, Nov 28, 2018 at 7:03 AM Junio C Hamano <gitster@pobox.com> wrote:\n> > >\n> > > Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n> > >\n> > > > The good old \"git checkout\" command is still here and will be until\n> > > > all (or most of users) are sick of it.\n> > >\n> > > Two comments on the goal (the implementation looked reasonable\n> > > assuming the reader agrees with the gaol).\n> > >\n> > > At least to me, the verb \"switch\" needs two things to switch\n> > > between, i.e. \"switch A and B\", unless it is \"switch to X\".\n> > > Either \"switch-to-branch\" or simply \"switch-to\", perhaps?\n> > >\n> > > As I already hinted in my response to Stefan (?) about\n> > > checkout-from-tree vs checkout-from-index, a command with multiple\n> > > modes of operation is not confusing to people with the right mental\n> > > model, and I suspect that having two separate commands for \"checking\n> > > out a branch\" and \"checking out paths\" that is done by this step\n> > > would help users to form the right mental model.\n> >\n> > Since the other one is already \"checkout-files\", maybe this one could\n> > just be \"checkout-branch\".\n>\n> I dislike the checkout-* names, as we already have checkout-index\n> as plumbing, so it would be confusing as to which checkout-* command\n> should be used when and why as it seems the co-index moves\n> content *from index* to the working tree, but the co-files moves content\n> *to files*, whereas checkout-branch is neither 'moving' to or from a branch\n> but rather 'switching' to that branch.\n\nOK back to square one. Another thing I noticed, not sure if it\nmatters, is that these two commands will be the only ones with a\nhyphen in them in \"git help\". But I guess it's even harder to find\none-word command names for these.\n-- \nDuy\n"},{"id":"364243","messageId":"CACsJy8D2gxPj4u0_eEg-_x-Z3S3+5FdTU6Su4VQM113nQq=PYg@mail.gmail.com","threadId":"49793","inReplyTo":"20181127165211.24763-1-pclouds@gmail.com","subject":"Re: [PATCH/RFC v2 0/7] Introduce new commands switch-branch and checkout-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-28T20:01:09Z","receivedAt":"2018-11-28T20:01:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Nov 27, 2018 at 5:53 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> v2 is just a bit better to look at than v1. This is by no means final.\n> If you think the command name is bad, the default behavior should\n> change, or something else, speak up. It's still very \"RFC\".\n>\n> v2 breaks down the giant patch in v1 and starts adding some changes in\n> these new commands:\n>\n> - restore-paths is renamed to checkout-paths. I wrote I didn't like\n>   \"checkout\" because of completion conflict. But who am I kidding,\n>   I'll use aliases anyway. \"-files\" instead of \"-paths\" because we\n>   already have ls-files.\n> - both commands will not accept no arguments. There is no \"git\n>   checkout\" equivalent.\n> - ambiguation rules are now aware that \"switch-branch\" for example\n>   can't take pathspec...\n\nAnother thing. Since we start with a clean slate, should we do\nsomething about detached HEAD in this switch-branch command (or\nwhatever its name will be)?\n\nThis is usually a confusing concept to new users and I've seen some\nusers have a hard time getting out of it. I'm too comfortable with\ndetached HEAD to see this from new user's perspective and a better way\nto deal with it.\n-- \nDuy\n"},{"id":"364244","messageId":"CACsJy8Cv9ZwWEs-wsOtms3JCXo7x8fL_PBatcb0TgvrrQuMUdg@mail.gmail.com","threadId":"49793","inReplyTo":"CACsJy8D2gxPj4u0_eEg-_x-Z3S3+5FdTU6Su4VQM113nQq=PYg@mail.gmail.com","subject":"Re: [PATCH/RFC v2 0/7] Introduce new commands switch-branch and checkout-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-28T20:09:22Z","receivedAt":"2018-11-28T20:09:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Nov 28, 2018 at 9:01 PM Duy Nguyen <pclouds@gmail.com> wrote:\n> should we do\n> something about detached HEAD in this switch-branch command (or\n> whatever its name will be)?\n>\n> This is usually a confusing concept to new users\n\nAnd it just occurred to me that perhaps we should call this \"unnamed\nbranch\" (at least at high UI level) instead of detached HEAD. It is\ntechnically not as accurate, but much better to understand.\n-- \nDuy\n"},{"id":"364249","messageId":"CAGZ79kYiMObOHAuf+01r0-YVWHBk-6NpceXh9Z25dx9JZsP62Q@mail.gmail.com","threadId":"49793","inReplyTo":"CACsJy8Cv9ZwWEs-wsOtms3JCXo7x8fL_PBatcb0TgvrrQuMUdg@mail.gmail.com","subject":"Re: [PATCH/RFC v2 0/7] Introduce new commands switch-branch and checkout-files","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-11-28T20:30:15Z","receivedAt":"2018-11-28T20:30:30Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Wed, Nov 28, 2018 at 12:09 PM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Wed, Nov 28, 2018 at 9:01 PM Duy Nguyen <pclouds@gmail.com> wrote:\n> > should we do\n> > something about detached HEAD in this switch-branch command (or\n> > whatever its name will be)?\n> >\n> > This is usually a confusing concept to new users\n>\n> And it just occurred to me that perhaps we should call this \"unnamed\n> branch\" (at least at high UI level) instead of detached HEAD. It is\n> technically not as accurate, but much better to understand.\n\nor 'direct' branch? I mean 'detached HEAD' itself is also not correct\nas the HEAD points to a valid commit/tag usually, so it is attached to\nthat content. The detachment comes from the implicit \"from a branch\".\n"},{"id":"364262","messageId":"CAPL8ZivFJXQw=yLXjOPxsjabwN5XAP_qe0LK3sODO4NkgCjZag@mail.gmail.com","threadId":"49793","inReplyTo":"CAPL8Ziuj7Ffmdvz6NZWSJ+vzAtxFQhO1cfY2wmXm16J_8sY5fw@mail.gmail.com","subject":"Re: [PATCH/RFC v2 0/7] Introduce new commands switch-branch and checkout-files","fromName":"Stefan Xenos","fromEmail":"sxenos@google.com","sentAt":"2018-11-28T22:53:39Z","receivedAt":"2018-11-28T22:53:56Z","isPatch":true,"sender":{"key":"sxenos@google.com","avatar":null},"body":"I think users have problems with detached heads for several reasons:\n\n1. Users often enter the detached head state unexpectedly (for\nexample, by mistyping a \"checkout\" command or not understanding its\nmultipurpose nature, or as a side-effect of running a submodule\ncommand). The change described here will go in the right direction\ntowards addressing this, since you won't be able to accidentally\ndetach your head by mistyping checkout. If detaching was always an\nintentional action by the user, it would be much less intimidating.\n\n2. Git shows a lot of scary error messages when running in a detached\nhead state. They tell you you're doing something dangerous, which\ndissuades users from experimenting with the feature. IMO, it would be\nbetter to replace the scary warning messages with instructions on\nwhere to get more information about detached heads (and those\ninstructions should frontload info on how to reattach to a branch and\nhow to recover commits from the reflog).\n\n3. When in a detached head state, a lot of commands add extra\nconfirmation prompts, which are unnecessary and intimidating.\n\nBasically, the current user interface implies to the user that a\ndetached head is an error condition they'll need to recover from\nrather than the normal and useful mode of working that it is.\n\n(I'm resending this email without html - sorry if you received it twice).\n\nSo - IMO - detaching should always be an explicit action. Some options\nthat occur to me:\n\ngit switch-branch --detach\ngit detach\ngit switch-branch HEAD\n\n  - Stefan\n\nOn Wed, Nov 28, 2018 at 2:48 PM Stefan Xenos <sxenos@google.com> wrote:\n>\n> I think users have problems with detached heads for several reasons:\n>\n> 1. Users often enter the detached head state unexpectedly (for example, by mistyping a \"checkout\" command or not understanding its multipurpose nature, or as a side-effect of running a submodule command). The change described here will go in the right direction towards addressing this, since you won't be able to accidentally detach your head by mistyping checkout. If detaching was always an intentional action by the user, it would be much less intimidating.\n>\n> 2. Git shows a lot of scary error messages when running in a detached head state. They tell you you're doing something dangerous, which dissuades users from experimenting with the feature. IMO, it would be better to replace the scary warning messages with instructions on where to get more information about detached heads (and those instructions should frontload info on how to reattach to a branch and how to recover commits from the reflog).\n>\n> 3. When in a detached head state, a lot of commands add extra confirmation prompts, which are unnecessary and intimidating.\n>\n> Basically, the current user interface implies to the user that a detached head is an error condition they'll need to recover from rather than the normal and useful mode of working that it is.\n>\n>   - Stefan\n>\n> On Wed, Nov 28, 2018 at 12:01 PM Duy Nguyen <pclouds@gmail.com> wrote:\n>>\n>> On Tue, Nov 27, 2018 at 5:53 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>> >\n>> > v2 is just a bit better to look at than v1. This is by no means final.\n>> > If you think the command name is bad, the default behavior should\n>> > change, or something else, speak up. It's still very \"RFC\".\n>> >\n>> > v2 breaks down the giant patch in v1 and starts adding some changes in\n>> > these new commands:\n>> >\n>> > - restore-paths is renamed to checkout-paths. I wrote I didn't like\n>> >   \"checkout\" because of completion conflict. But who am I kidding,\n>> >   I'll use aliases anyway. \"-files\" instead of \"-paths\" because we\n>> >   already have ls-files.\n>> > - both commands will not accept no arguments. There is no \"git\n>> >   checkout\" equivalent.\n>> > - ambiguation rules are now aware that \"switch-branch\" for example\n>> >   can't take pathspec...\n>>\n>> Another thing. Since we start with a clean slate, should we do\n>> something about detached HEAD in this switch-branch command (or\n>> whatever its name will be)?\n>>\n>> This is usually a confusing concept to new users and I've seen some\n>> users have a hard time getting out of it. I'm too comfortable with\n>> detached HEAD to see this from new user's perspective and a better way\n>> to deal with it.\n>> --\n>> Duy\n"},{"id":"364263","messageId":"CAPL8ZiuaEW5tp8ZMOZtZcb5oi3L-pDF6ajcA7b5wnH3=7Ls7Tg@mail.gmail.com","threadId":"49793","inReplyTo":"CACsJy8Bzs=FYKrR6h1cqVH32eEt2t8rUMtE2yFNvt+W55u=sDA@mail.gmail.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Stefan Xenos","fromEmail":"sxenos@google.com","sentAt":"2018-11-28T23:22:29Z","receivedAt":"2018-11-28T23:22:45Z","isPatch":true,"sender":{"key":"sxenos@google.com","avatar":null},"body":"> Since the other one is already \"checkout-files\", maybe this one could just be \"checkout-branch\".\n\nI rather like switch-branch and dislike the word \"checkout\" since it\nhas been overloaded in git for so long (does it mean moving HEAD or\ncopying files to my working tree?)\n\n> nobody will become \"sick of\" the single \"checkout\" command that can\n\nI have to admit I'm already sick of the checkout command. :-p I can\nsee myself using these two new commands 100% of the time and never\nmissing the old one.\n\nSome behaviors I'd expect to see from these commands (I haven't yet\nchecked to see if you've already done this):\n\ngit checkout-files <tree-ish>\nshould reset all the files in the repository regardless of the current\ndirectory - it should produce the same effect as \"git reset --hard\n<tree-ish> && git reset HEAD@{1}\". It should also delete\nlocally-created files that aren't present in <tree-ish>, such that the\nfinal working tree is exactly identical to what was committed in that\ntree-ish.\n\ngit checkout-files foo -- myfile.txt\nshould delete myfile.txt if it is present locally but not present in foo.\n\ngit checkout-files foo -- .\nshould recursively checkout all files in the current folder and all\nsubfolders, and delete any locally-created files if they're not\npresent in foo.\n\ngit checkout-files should never move HEAD in any circumstance.\n\nSuggestion:\nIf git checkout-files overwrites or deletes any locally-modified files\nfrom the workspace or index, those files could be auto-stashed. That\nwould make it easy to restore them in the event of a mistyped command.\nAuto-stashing could be suppressed with a command-line argument (with\nalternate behaviors being fail-if-modified or always-overwrite).\n\nIdea:\nIf git checkout-files modifies the submodules file, it could also\nauto-update the submodules. (For example, with something like \"git\nsubmodule update --init --recursive --progress\").\n\n  - Stefan\nOn Wed, Nov 28, 2018 at 7:31 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Wed, Nov 28, 2018 at 7:03 AM Junio C Hamano <gitster@pobox.com> wrote:\n> >\n> > Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n> >\n> > > The good old \"git checkout\" command is still here and will be until\n> > > all (or most of users) are sick of it.\n> >\n> > Two comments on the goal (the implementation looked reasonable\n> > assuming the reader agrees with the gaol).\n> >\n> > At least to me, the verb \"switch\" needs two things to switch\n> > between, i.e. \"switch A and B\", unless it is \"switch to X\".\n> > Either \"switch-to-branch\" or simply \"switch-to\", perhaps?\n> >\n> > As I already hinted in my response to Stefan (?) about\n> > checkout-from-tree vs checkout-from-index, a command with multiple\n> > modes of operation is not confusing to people with the right mental\n> > model, and I suspect that having two separate commands for \"checking\n> > out a branch\" and \"checking out paths\" that is done by this step\n> > would help users to form the right mental model.\n>\n> Since the other one is already \"checkout-files\", maybe this one could\n> just be \"checkout-branch\".\n>\n> > So I tend to think\n> > these two are \"training wheels\", and suspect that once they got it,\n> > nobody will become \"sick of\" the single \"checkout\" command that can\n> > be used to do either.  It's just the matter of being aware what can\n> > be done (which requires the right mental model) and how to tell Git\n> > what the user wants it do (two separate commands, operating mode\n> > option, or just the implied command line syntax---once the user\n> > knows what s/he is doing, these do not make that much a difference).\n>\n> I would hope this becomes better defaults and being used 90% of time.\n> Even though I know \"git checkout\" quite well, it still bites me from\n> time to time. Having the right mental model is one thing. Having to\n> think a bit every time to write \"git checkout\" with the right syntax,\n> and whether you need \"--\" (that ambiguation problem can still bite you\n> from time to time), is frankly something I'd rather avoid.\n> --\n> Duy\n"},{"id":"364264","messageId":"CAPL8ZivJ+=Y=8pxvs3sJrdxVtkn9xfTA63GeHcr=J0Y2JscOMQ@mail.gmail.com","threadId":"49793","inReplyTo":"CAPL8ZiuaEW5tp8ZMOZtZcb5oi3L-pDF6ajcA7b5wnH3=7Ls7Tg@mail.gmail.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Stefan Xenos","fromEmail":"sxenos@google.com","sentAt":"2018-11-28T23:26:50Z","receivedAt":"2018-11-28T23:27:07Z","isPatch":true,"sender":{"key":"sxenos@google.com","avatar":null},"body":"Although I have no problem with \"switch-branch\" as a command name,\nsome alternative names we might consider for switch-branch might be:\n\nchbranch\nswbranch\nswitch\nbranch change (as a subcommand for the \"branch\" command)\n\nI've personally been using \"chbranch\" as an alias for this\nfunctionality for some time.\n\n  - Stefan\nOn Wed, Nov 28, 2018 at 3:22 PM Stefan Xenos <sxenos@google.com> wrote:\n>\n> > Since the other one is already \"checkout-files\", maybe this one could just be \"checkout-branch\".\n>\n> I rather like switch-branch and dislike the word \"checkout\" since it\n> has been overloaded in git for so long (does it mean moving HEAD or\n> copying files to my working tree?)\n>\n> > nobody will become \"sick of\" the single \"checkout\" command that can\n>\n> I have to admit I'm already sick of the checkout command. :-p I can\n> see myself using these two new commands 100% of the time and never\n> missing the old one.\n>\n> Some behaviors I'd expect to see from these commands (I haven't yet\n> checked to see if you've already done this):\n>\n> git checkout-files <tree-ish>\n> should reset all the files in the repository regardless of the current\n> directory - it should produce the same effect as \"git reset --hard\n> <tree-ish> && git reset HEAD@{1}\". It should also delete\n> locally-created files that aren't present in <tree-ish>, such that the\n> final working tree is exactly identical to what was committed in that\n> tree-ish.\n>\n> git checkout-files foo -- myfile.txt\n> should delete myfile.txt if it is present locally but not present in foo.\n>\n> git checkout-files foo -- .\n> should recursively checkout all files in the current folder and all\n> subfolders, and delete any locally-created files if they're not\n> present in foo.\n>\n> git checkout-files should never move HEAD in any circumstance.\n>\n> Suggestion:\n> If git checkout-files overwrites or deletes any locally-modified files\n> from the workspace or index, those files could be auto-stashed. That\n> would make it easy to restore them in the event of a mistyped command.\n> Auto-stashing could be suppressed with a command-line argument (with\n> alternate behaviors being fail-if-modified or always-overwrite).\n>\n> Idea:\n> If git checkout-files modifies the submodules file, it could also\n> auto-update the submodules. (For example, with something like \"git\n> submodule update --init --recursive --progress\").\n>\n>   - Stefan\n> On Wed, Nov 28, 2018 at 7:31 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> >\n> > On Wed, Nov 28, 2018 at 7:03 AM Junio C Hamano <gitster@pobox.com> wrote:\n> > >\n> > > Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n> > >\n> > > > The good old \"git checkout\" command is still here and will be until\n> > > > all (or most of users) are sick of it.\n> > >\n> > > Two comments on the goal (the implementation looked reasonable\n> > > assuming the reader agrees with the gaol).\n> > >\n> > > At least to me, the verb \"switch\" needs two things to switch\n> > > between, i.e. \"switch A and B\", unless it is \"switch to X\".\n> > > Either \"switch-to-branch\" or simply \"switch-to\", perhaps?\n> > >\n> > > As I already hinted in my response to Stefan (?) about\n> > > checkout-from-tree vs checkout-from-index, a command with multiple\n> > > modes of operation is not confusing to people with the right mental\n> > > model, and I suspect that having two separate commands for \"checking\n> > > out a branch\" and \"checking out paths\" that is done by this step\n> > > would help users to form the right mental model.\n> >\n> > Since the other one is already \"checkout-files\", maybe this one could\n> > just be \"checkout-branch\".\n> >\n> > > So I tend to think\n> > > these two are \"training wheels\", and suspect that once they got it,\n> > > nobody will become \"sick of\" the single \"checkout\" command that can\n> > > be used to do either.  It's just the matter of being aware what can\n> > > be done (which requires the right mental model) and how to tell Git\n> > > what the user wants it do (two separate commands, operating mode\n> > > option, or just the implied command line syntax---once the user\n> > > knows what s/he is doing, these do not make that much a difference).\n> >\n> > I would hope this becomes better defaults and being used 90% of time.\n> > Even though I know \"git checkout\" quite well, it still bites me from\n> > time to time. Having the right mental model is one thing. Having to\n> > think a bit every time to write \"git checkout\" with the right syntax,\n> > and whether you need \"--\" (that ambiguation problem can still bite you\n> > from time to time), is frankly something I'd rather avoid.\n> > --\n> > Duy\n"},{"id":"364265","messageId":"CAPL8ZisWR_uzoy2wYqgpNfxNvcjD7wvB4HN90FT9aeK54DCd=Q@mail.gmail.com","threadId":"49793","inReplyTo":"CAPL8ZivJ+=Y=8pxvs3sJrdxVtkn9xfTA63GeHcr=J0Y2JscOMQ@mail.gmail.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Stefan Xenos","fromEmail":"sxenos@google.com","sentAt":"2018-11-28T23:37:56Z","receivedAt":"2018-11-28T23:38:12Z","isPatch":true,"sender":{"key":"sxenos@google.com","avatar":null},"body":"More thoughts:\n\ngit switch-branch should never detach HEAD unless asked to do so\nexplicitly. That also means that \"git switch-branch\" shouldn't accept\nany of the non-branch tree-ish arguments that would have caused \"git\ncheckout\" to do so.\nOn Wed, Nov 28, 2018 at 3:26 PM Stefan Xenos <sxenos@google.com> wrote:\n>\n> Although I have no problem with \"switch-branch\" as a command name,\n> some alternative names we might consider for switch-branch might be:\n>\n> chbranch\n> swbranch\n> switch\n> branch change (as a subcommand for the \"branch\" command)\n>\n> I've personally been using \"chbranch\" as an alias for this\n> functionality for some time.\n>\n>   - Stefan\n> On Wed, Nov 28, 2018 at 3:22 PM Stefan Xenos <sxenos@google.com> wrote:\n> >\n> > > Since the other one is already \"checkout-files\", maybe this one could just be \"checkout-branch\".\n> >\n> > I rather like switch-branch and dislike the word \"checkout\" since it\n> > has been overloaded in git for so long (does it mean moving HEAD or\n> > copying files to my working tree?)\n> >\n> > > nobody will become \"sick of\" the single \"checkout\" command that can\n> >\n> > I have to admit I'm already sick of the checkout command. :-p I can\n> > see myself using these two new commands 100% of the time and never\n> > missing the old one.\n> >\n> > Some behaviors I'd expect to see from these commands (I haven't yet\n> > checked to see if you've already done this):\n> >\n> > git checkout-files <tree-ish>\n> > should reset all the files in the repository regardless of the current\n> > directory - it should produce the same effect as \"git reset --hard\n> > <tree-ish> && git reset HEAD@{1}\". It should also delete\n> > locally-created files that aren't present in <tree-ish>, such that the\n> > final working tree is exactly identical to what was committed in that\n> > tree-ish.\n> >\n> > git checkout-files foo -- myfile.txt\n> > should delete myfile.txt if it is present locally but not present in foo.\n> >\n> > git checkout-files foo -- .\n> > should recursively checkout all files in the current folder and all\n> > subfolders, and delete any locally-created files if they're not\n> > present in foo.\n> >\n> > git checkout-files should never move HEAD in any circumstance.\n> >\n> > Suggestion:\n> > If git checkout-files overwrites or deletes any locally-modified files\n> > from the workspace or index, those files could be auto-stashed. That\n> > would make it easy to restore them in the event of a mistyped command.\n> > Auto-stashing could be suppressed with a command-line argument (with\n> > alternate behaviors being fail-if-modified or always-overwrite).\n> >\n> > Idea:\n> > If git checkout-files modifies the submodules file, it could also\n> > auto-update the submodules. (For example, with something like \"git\n> > submodule update --init --recursive --progress\").\n> >\n> >   - Stefan\n> > On Wed, Nov 28, 2018 at 7:31 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> > >\n> > > On Wed, Nov 28, 2018 at 7:03 AM Junio C Hamano <gitster@pobox.com> wrote:\n> > > >\n> > > > Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n> > > >\n> > > > > The good old \"git checkout\" command is still here and will be until\n> > > > > all (or most of users) are sick of it.\n> > > >\n> > > > Two comments on the goal (the implementation looked reasonable\n> > > > assuming the reader agrees with the gaol).\n> > > >\n> > > > At least to me, the verb \"switch\" needs two things to switch\n> > > > between, i.e. \"switch A and B\", unless it is \"switch to X\".\n> > > > Either \"switch-to-branch\" or simply \"switch-to\", perhaps?\n> > > >\n> > > > As I already hinted in my response to Stefan (?) about\n> > > > checkout-from-tree vs checkout-from-index, a command with multiple\n> > > > modes of operation is not confusing to people with the right mental\n> > > > model, and I suspect that having two separate commands for \"checking\n> > > > out a branch\" and \"checking out paths\" that is done by this step\n> > > > would help users to form the right mental model.\n> > >\n> > > Since the other one is already \"checkout-files\", maybe this one could\n> > > just be \"checkout-branch\".\n> > >\n> > > > So I tend to think\n> > > > these two are \"training wheels\", and suspect that once they got it,\n> > > > nobody will become \"sick of\" the single \"checkout\" command that can\n> > > > be used to do either.  It's just the matter of being aware what can\n> > > > be done (which requires the right mental model) and how to tell Git\n> > > > what the user wants it do (two separate commands, operating mode\n> > > > option, or just the implied command line syntax---once the user\n> > > > knows what s/he is doing, these do not make that much a difference).\n> > >\n> > > I would hope this becomes better defaults and being used 90% of time.\n> > > Even though I know \"git checkout\" quite well, it still bites me from\n> > > time to time. Having the right mental model is one thing. Having to\n> > > think a bit every time to write \"git checkout\" with the right syntax,\n> > > and whether you need \"--\" (that ambiguation problem can still bite you\n> > > from time to time), is frankly something I'd rather avoid.\n> > > --\n> > > Duy\n"},{"id":"364287","messageId":"xmqqh8g0pgwb.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CAGZ79kbWqfMHZeYFXNh00N5xSSkW0_Mzja1EtuzxQxrhESoZxQ@mail.gmail.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-29T05:55:00Z","receivedAt":"2018-11-29T05:55:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> I dislike the checkout-* names, as we already have checkout-index\n> as plumbing, so it would be confusing as to which checkout-* command\n> should be used when and why as it seems the co-index moves\n> content *from index* to the working tree, but the co-files moves content\n> *to files*, whereas checkout-branch is neither 'moving' to or from a branch\n> but rather 'switching' to that branch.\n\nTo me, \"switching to work on the branch\", is like \"checking the book\nout from the library to read\".  IOW, \"check the branch out to work\non\" does not have to involve *any* moving of contents.\n"},{"id":"364288","messageId":"xmqqd0qopgpn.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CAPL8ZivJ+=Y=8pxvs3sJrdxVtkn9xfTA63GeHcr=J0Y2JscOMQ@mail.gmail.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-29T05:59:00Z","receivedAt":"2018-11-29T05:59:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Xenos <sxenos@google.com> writes:\n\n> Although I have no problem with \"switch-branch\" as a command name,\n> some alternative names we might consider for switch-branch might be:\n>\n> chbranch\n> swbranch\n\nPlease never go in that direction.  So far, we made a conscious\neffort to keep the names of most frequently used subcommand to\nproper words that can be understood by coders (IOW, I expect they\nknow what 'grep' is, even though that may not be a 'proper word').\n\n> switch\n> branch change (as a subcommand for the \"branch\" command)\n\nIt is more like moving to the branch to work on.  I think 'switch'\nis something SVN veterans may find familiar.  Both are much better\nthan ch/swbranch that are overly cryptic.\n"},{"id":"364289","messageId":"xmqq36rkpgec.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CACsJy8BBeRDY85rJLzeJRFSazEn1rZ8JGN6_vwAr2ia=FaiLNg@mail.gmail.com","subject":"Re: [PATCH v2 7/7] Suggest other commands instead of \"git checkout\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-29T06:05:47Z","receivedAt":"2018-11-29T06:05:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> I see my deliberate attempt to provoke has failed :D Giving your view\n> of the new commands as \"training wheels\", I take it we still should\n> make them visible as much as possible, but we just not try to hide\n> \"git checkout\" as much (e.g. we mention both new and old commands,\n> instead of just mentioning the new one, when suggesting something)?\n\nYes, I do support the overall idea of learning two (or possibly\nthree) separate commands would help new users to form the right\nmental model much sooner than learning one that can be used in\nmultiple ways.  Another possible approach could be to split the use\nof \"reset\" that does not move \"HEAD\" into the same half of\n\"checkout\" that does not move \"HEAD\", i.e. checkout-files.\n"},{"id":"364290","messageId":"xmqqwoowo1fk.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CAPL8ZivFJXQw=yLXjOPxsjabwN5XAP_qe0LK3sODO4NkgCjZag@mail.gmail.com","subject":"Re: [PATCH/RFC v2 0/7] Introduce new commands switch-branch and checkout-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-29T06:14:23Z","receivedAt":"2018-11-29T06:14:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Xenos <sxenos@google.com> writes:\n\n> So - IMO - detaching should always be an explicit action. Some options\n> that occur to me:\n>\n> git switch-branch --detach\n\nThat is the most obvious way to spell it, and it is why we have \"git\ncheckout --detach\".  If we were to split one half of \"checkout\" into\n\"switch-branch\", I would support the idea to make switch-branch only\ntake a branch name and nothing else, allow it to take any commit-ish\nto detach the HEAD at that commit in the history graph with the\n--detach option\".  I also do not think anybody minds explaining the\nresulting state to be \"on an unnamed branch\" (or is it \"the\" unnamed\nbranch?  Given that HEAD@{} reflog is a singleton, perhaps the right\nmental model is that there is only one unnamed branch per worktree).\n\n> git detach\n\nThe detached HEAD state is not all that special to deserve a\nseparate command.  After all, all history growing commands like\n\"commit\", \"cherry-pick\", \"rebase\", \"merge\", etc. work the same way\non the unnamed branch.\n\n> git switch-branch HEAD\n\nI do not think this one is acceptable.  \"git checkout HEAD\" does not\ndetach but works as if you said \"git checkout master\" (when on the\n'master' branch).  And you do not want \"git switch-branch HEAD^0\" to\nbe that explicit way to tell Git to detach the HEAD as that will\ntake us back to square one (\"git checkout HEAD^0\" is the more\nconcise and time-honored way to say \"git checkout --detach HEAD\").\n"},{"id":"364291","messageId":"xmqqsgzko0px.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"20181127165211.24763-6-pclouds@gmail.com","subject":"Re: [PATCH v2 5/7] checkout: split options[] array in three pieces","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-29T06:29:46Z","receivedAt":"2018-11-29T06:29:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> +static struct option *add_switch_branch_options(struct checkout_opts *opts,\n> +\t\t\t\t\t\tstruct option *prevopts)\n> +{\n> +\tstruct option options[] = {\n>  \t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n>  \t\t\t   N_(\"create and checkout a new branch\")),\n\nI think there should be another step to rename the options to more\nsensible ones for the context.  In the context of overall \"checkout\"\ncommand, the 'b' option\n\n\tgit checkout -b <new-name> <commit-ish>\"\n\nthat signals that its parameter has something to do with a 'branch'\nmakes perfect sense.  But when the user uses the new command\n\n\tgit switch-branch -b <new-name> <commit-ish>\n\ndoes not convey the more important fact in the context.  In the\norignal context, \"this is about a branch\" and \"we are not checking\nout an existing one, but are creating\" are both worthwhile thing to\nexpress, but because a single letter option cannot stand for both,\n\"-b\" is the most reasonable choice (compared to calling it \"-c\" that\nstands for \"create\" that invites \"what exactly are you creating?\").\n\nIn the new context of \"switch-branch\", it no longer has to be said\nthat the optional feature is about \"branch\".  So I would imagine\nthat users naturally expect this option to be called\n\n\tgit switch-branch --create <new-name> <commit-ish>\n\n(or -c for short).\n\nI'll just stop with this single example, but I think we should make\nsure all the options make sense in the context of new command.\n\nOf course, that means it will NOT be sufficient to just split the\noption table into two tables and stitch them together for the single\ncommand.  This option must stay to be \"-b\" (giving it a synonym\n\"--create-branch\" is OK) in the context of \"checkout\".\n"},{"id":"364333","messageId":"CACsJy8CkBV48Yd9FHfLVQSHJ630uw8icn128xjAPUOeWJVWfVA@mail.gmail.com","threadId":"49793","inReplyTo":"CAGZ79kYiMObOHAuf+01r0-YVWHBk-6NpceXh9Z25dx9JZsP62Q@mail.gmail.com","subject":"Re: [PATCH/RFC v2 0/7] Introduce new commands switch-branch and checkout-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T15:33:03Z","receivedAt":"2018-11-29T15:33:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Nov 28, 2018 at 9:30 PM Stefan Beller <sbeller@google.com> wrote:\n>\n> On Wed, Nov 28, 2018 at 12:09 PM Duy Nguyen <pclouds@gmail.com> wrote:\n> >\n> > On Wed, Nov 28, 2018 at 9:01 PM Duy Nguyen <pclouds@gmail.com> wrote:\n> > > should we do\n> > > something about detached HEAD in this switch-branch command (or\n> > > whatever its name will be)?\n> > >\n> > > This is usually a confusing concept to new users\n> >\n> > And it just occurred to me that perhaps we should call this \"unnamed\n> > branch\" (at least at high UI level) instead of detached HEAD. It is\n> > technically not as accurate, but much better to understand.\n>\n> or 'direct' branch?\n\nmakes me think, what is an indirect branch?\n\n> I mean 'detached HEAD' itself is also not correct\n> as the HEAD points to a valid commit/tag usually, so it is attached to\n> that content. The detachment comes from the implicit \"from a branch\".\n\nYeah I guess it's short for \"HEAD that is detached from a branch\"\n-- \nDuy\n"},{"id":"364334","messageId":"CACsJy8AQk--qoBBbniJF5uJZdX2P1=y+wBt-RzD28WsH4m2rkQ@mail.gmail.com","threadId":"49793","inReplyTo":"xmqqd0qopgpn.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T15:36:04Z","receivedAt":"2018-11-29T15:36:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Nov 29, 2018 at 6:59 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Stefan Xenos <sxenos@google.com> writes:\n>\n> > Although I have no problem with \"switch-branch\" as a command name,\n> > some alternative names we might consider for switch-branch might be:\n> >\n> > chbranch\n> > swbranch\n>\n> Please never go in that direction.  So far, we made a conscious\n> effort to keep the names of most frequently used subcommand to\n> proper words that can be understood by coders (IOW, I expect they\n> know what 'grep' is, even though that may not be a 'proper word').\n>\n> > switch\n> > branch change (as a subcommand for the \"branch\" command)\n>\n> It is more like moving to the branch to work on.  I think 'switch'\n> is something SVN veterans may find familiar.  Both are much better\n> than ch/swbranch that are overly cryptic.\n\nOK I'll go with switch-branch and restore-files in the next round. And\nperhaps consider just 'switch' and 'restore' later.\n-- \nDuy\n"},{"id":"364339","messageId":"CACsJy8An2n5yah1UTCJZoC5ucSpCoM0vrXtEXnjg-di7jQZwLA@mail.gmail.com","threadId":"49793","inReplyTo":"CAPL8ZiuaEW5tp8ZMOZtZcb5oi3L-pDF6ajcA7b5wnH3=7Ls7Tg@mail.gmail.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T15:46:04Z","receivedAt":"2018-11-29T15:46:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Nov 29, 2018 at 12:22 AM Stefan Xenos <sxenos@google.com> wrote:\n> Some behaviors I'd expect to see from these commands (I haven't yet\n> checked to see if you've already done this):\n>\n> git checkout-files <tree-ish>\n> should reset all the files in the repository regardless of the current\n> directory - it should produce the same effect as \"git reset --hard\n> <tree-ish> && git reset HEAD@{1}\". It should also delete\n> locally-created files that aren't present in <tree-ish>, such that the\n> final working tree is exactly identical to what was committed in that\n> tree-ish.\n>\n> git checkout-files foo -- myfile.txt\n> should delete myfile.txt if it is present locally but not present in foo.\n>\n> git checkout-files foo -- .\n> should recursively checkout all files in the current folder and all\n> subfolders, and delete any locally-created files if they're not\n> present in foo.\n\nI think all these are the same as the non-overlay mode Thomas\nmentioned [1]. Once he implements that in git-checkout, we can make it\ndefault in checkout-files.\n\nTwo things though. I plan to get rid of \"--\" in checkout-files. The\nmain use case (I think) is reset from index, so you can just write\n\n git checkout-files somefiles\n\nand if you want to get it from the tree(-ish) \"foo\", you do\n\n git checkout-files --from=foo somefiles\n\nThis form is easier to read (and even guess before you read man pages)\nand leaves no room for ambiguation.\n\nThe second thing is, I plan to forbid \"git checkout-files\" without\narguments. If you want to reset the entire worktree, do\n\n git checkout-files :/\n\nor just current dir\n\n git checkout-files .\n\nWhich brings us back to your \"git checkout-files <tree-ish>\" use case\nabove. It should be treat the same way in my opinion, so we either do\n\n git checkout-files --from=tree-ish :/\n\nor\n\n git checkout-files --from=tree-ish .\n\nBut \"git checkout-files --from=tree-ish\" alone is rejected.\n\n\n[1] https://public-inbox.org/git/xmqqwoowo1fk.fsf@gitster-ct.c.googlers.com/T/#mdb076d178ccf0ae3dba0fd63143f99278047da93\n\n> Suggestion:\n> If git checkout-files overwrites or deletes any locally-modified files\n> from the workspace or index, those files could be auto-stashed. That\n> would make it easy to restore them in the event of a mistyped command.\n> Auto-stashing could be suppressed with a command-line argument (with\n> alternate behaviors being fail-if-modified or always-overwrite).\n\nStashing I think is not the right tool for this. When you stash, you\nplan to retrieve it back later but here you should rarely ever need to\nunstash until the accident. For recovery from accidents like this, I\nhave another thing in queue to achieve the same (I'm calling it\n\"backup log\" now). So we will have the same functionality, just with\ndifferent means.\n\n> Idea:\n> If git checkout-files modifies the submodules file, it could also\n> auto-update the submodules. (For example, with something like \"git\n> submodule update --init --recursive --progress\").\n\nThis one is tricky because we should deal with submodule autoupdate\nconsistently across all porcelain commands (or at least common ones),\nnot just checkout-files. I'd prefer to delay this until later. Once we\nfigure out what to do with other commands, then we can still change\ndefaults for checkout-files.\n-- \nDuy\n"},{"id":"364346","messageId":"CAGZ79kY2Fu_3b8MnO-yV_JhevZRgx7=6ndX-N_R2HdJGByciHA@mail.gmail.com","threadId":"49793","inReplyTo":"CACsJy8An2n5yah1UTCJZoC5ucSpCoM0vrXtEXnjg-di7jQZwLA@mail.gmail.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-11-29T18:14:40Z","receivedAt":"2018-11-29T18:14:56Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"> > Idea:\n> > If git checkout-files modifies the submodules file, it could also\n> > auto-update the submodules. (For example, with something like \"git\n> > submodule update --init --recursive --progress\").\n>\n> This one is tricky because we should deal with submodule autoupdate\n> consistently across all porcelain commands (or at least common ones),\n> not just checkout-files. I'd prefer to delay this until later. Once we\n> figure out what to do with other commands, then we can still change\n> defaults for checkout-files.\n\ncheckout/reset are respecting the submodule.recurse setting for this\nalready, and as your patches only change the UX frontend\n\n    git -c submodule.recurse checkout-files <pathsspec>\n\nwould also touch submodules. Given that deep down in\nthe submodules it's all about files again, we could think\ncheckout-files is still a good name.\n\nI think Stefan X. is asking for making submodule.recurse\nto default to true, which is indeed unrelated to this.\n\nStefan\n"},{"id":"364347","messageId":"CACsJy8C8pf=VQk54svj0ahQ3Uhhf_-FeGxUfBQofAcnDUzQzMQ@mail.gmail.com","threadId":"49793","inReplyTo":"CAGZ79kY2Fu_3b8MnO-yV_JhevZRgx7=6ndX-N_R2HdJGByciHA@mail.gmail.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T18:30:34Z","receivedAt":"2018-11-29T18:31:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Nov 29, 2018 at 7:14 PM Stefan Beller <sbeller@google.com> wrote:\n>\n> > > Idea:\n> > > If git checkout-files modifies the submodules file, it could also\n> > > auto-update the submodules. (For example, with something like \"git\n> > > submodule update --init --recursive --progress\").\n> >\n> > This one is tricky because we should deal with submodule autoupdate\n> > consistently across all porcelain commands (or at least common ones),\n> > not just checkout-files. I'd prefer to delay this until later. Once we\n> > figure out what to do with other commands, then we can still change\n> > defaults for checkout-files.\n>\n> checkout/reset are respecting the submodule.recurse setting for this\n> already, and as your patches only change the UX frontend\n>\n>     git -c submodule.recurse checkout-files <pathsspec>\n>\n> would also touch submodules. Given that deep down in\n> the submodules it's all about files again, we could think\n> checkout-files is still a good name.\n>\n> I think Stefan X. is asking for making submodule.recurse\n> to default to true, which is indeed unrelated to this.\n\nYes and I'm concerned that checkout-files now recurses into submodules\nthis by default but grep for example does not. That just adds more\nconfusion.\n-- \nDuy\n"},{"id":"364352","messageId":"CAPL8ZitNd+1zadorvrELSHqet5cFBU9UyigA6JGwHQOWmX_fWQ@mail.gmail.com","threadId":"49793","inReplyTo":"CACsJy8An2n5yah1UTCJZoC5ucSpCoM0vrXtEXnjg-di7jQZwLA@mail.gmail.com","subject":"Re: [PATCH v2 6/7] checkout: split into switch-branch and checkout-files","fromName":"Stefan Xenos","fromEmail":"sxenos@google.com","sentAt":"2018-11-29T19:29:40Z","receivedAt":"2018-11-29T19:29:56Z","isPatch":true,"sender":{"key":"sxenos@google.com","avatar":null},"body":">\n> Which brings us back to your \"git checkout-files <tree-ish>\" use case\n> above. It should be treat the same way in my opinion, so we either do\n>\n>  git checkout-files --from=tree-ish :/\n>\n> or\n>\n>  git checkout-files --from=tree-ish .\n>\n> But \"git checkout-files --from=tree-ish\" alone is rejected.\n\nAgreed. Those arguments are better. The gist of my comment was the\ntreatment of newly created local files rather than the form of the\narguments, but it sounds like you've got that under control, too.\n\n> > Suggestion:\n> > If git checkout-files overwrites or deletes any locally-modified files\n> > from the workspace or index, those files could be auto-stashed. That\n> > would make it easy to restore them in the event of a mistyped command.\n> > Auto-stashing could be suppressed with a command-line argument (with\n> > alternate behaviors being fail-if-modified or always-overwrite).\n>\n> Stashing I think is not the right tool for this. When you stash, you\n> plan to retrieve it back later but here you should rarely ever need to\n> unstash until the accident. For recovery from accidents like this, I\n> have another thing in queue to achieve the same (I'm calling it\n> \"backup log\" now). So we will have the same functionality, just with\n> different means.\n\nYes, this makes sense too. You wouldn't want to pollute the stash list\nwith autogenerated things the user probably doesn't want.\n\n> This one is tricky because we should deal with submodule autoupdate\n> consistently across all porcelain commands (or at least common ones),\n> not just checkout-files.\n\nThis is also a good point. I'd like it if submodules just behaved like\na single giant repository for most commands, but you're right that\nthis is something that should be done intentionally for all the\ncommands at one rather than just for a single command.\n\nI also like your new names \"switch-branch\" and \"restore-files\".\n"},{"id":"364355","messageId":"20181129215850.7278-1-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181127165211.24763-1-pclouds@gmail.com","subject":"[PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:35Z","receivedAt":"2018-11-29T21:59:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v3 sees switch-branch go back to switch-branch (in v2 it was\ncheckout-branch). checkout-files is also renamed restore-files (v1 was\nrestore-paths). Hopefully we won't see another rename.\n\nI'll try to summarize the differences between the new commands and\n'git checkout' down here, but you're welcome to just head to 07/14 and\nread the new man pages.\n\n'git switch-branch'\n\n- does not \"do nothing\", you have to either switch branch, create a\n  new branch, or detach. \"git switch-branch\" with no arguments is\n  rejected.\n\n- implicit detaching is rejected. If you need to detach, you need to\n  give --detach. Or stick to 'git checkout'.\n\n- -b/-B is renamed to -c/-C with long option names\n\n- of course does not accept pathspec\n\n'git restore-files'\n\n- takes a ref from --from argument, not as a free ref. As a result,\n  '--' is no longer needed. All non-option arguments are pathspec\n\n- pathspec is mandatory, you can't do \"git restore-files\" without any\n  pathspec.\n\n- I just remember -p which is allowed to take no pathspec :( I'll fix\n  it later.\n\n- Two more fancy features (the \"git checkout --index\" being the\n  default mode and the backup log for accidental overwrites) are of\n  course still missing. But they are coming.\n\nI did not go replace \"detached HEAD\" with \"unnamed branch\" (or \"no\nbranch\") everywhere because I think a unique term is still good to\nrefer to this concept. Or maybe \"no branch\" is good enough. I dunno.\n\nNguyễn Thái Ngọc Duy (14):\n  git-checkout.txt: fix one syntax line\n  git-checkout.txt: split detached head section out\n  checkout: factor out some code in parse_branchname_arg()\n  checkout: make \"opts\" in cmd_checkout() a pointer\n  checkout: move 'confict_style' and 'dwim_..' to checkout_opts\n  checkout: split options[] array in three pieces\n  checkout: split into switch-branch and restore-files\n  switch-branch: better names for -b and -B\n  switch-branch: stop accepting pathspec\n  switch-branch: reject \"do nothing\" case\n  switch-branch: only allow explicit detached HEAD\n  restore-files: take tree-ish from --from option instead\n  restore-files: make pathspec mandatory\n  doc: promote \"git switch-branch\" and \"git restore-files\"\n\n .gitignore                             |   2 +\n Documentation/config/advice.txt        |  10 +-\n Documentation/config/checkout.txt      |   5 +-\n Documentation/detach-head.txt          | 132 +++++++++\n Documentation/git-branch.txt           |   8 +-\n Documentation/git-check-ref-format.txt |   2 +-\n Documentation/git-checkout.txt         | 140 +--------\n Documentation/git-format-patch.txt     |   2 +-\n Documentation/git-merge-base.txt       |   2 +-\n Documentation/git-rebase.txt           |   2 +-\n Documentation/git-remote.txt           |   2 +-\n Documentation/git-rerere.txt           |  10 +-\n Documentation/git-reset.txt            |  18 +-\n Documentation/git-restore-files.txt    | 167 +++++++++++\n Documentation/git-revert.txt           |   2 +-\n Documentation/git-stash.txt            |   6 +-\n Documentation/git-switch-branch.txt    | 289 +++++++++++++++++++\n Documentation/gitattributes.txt        |   2 +-\n Documentation/gitcli.txt               |   4 +-\n Documentation/gitcore-tutorial.txt     |  18 +-\n Documentation/giteveryday.txt          |  24 +-\n Documentation/githooks.txt             |   5 +-\n Documentation/gittutorial-2.txt        |   2 +-\n Documentation/gittutorial.txt          |   4 +-\n Documentation/revisions.txt            |   2 +-\n Documentation/user-manual.txt          |  54 ++--\n Makefile                               |   2 +\n advice.c                               |  11 +-\n builtin.h                              |   2 +\n builtin/checkout.c                     | 380 ++++++++++++++++++-------\n command-list.txt                       |   4 +-\n git.c                                  |   2 +\n parse-options-cb.c                     |  16 ++\n parse-options.h                        |   3 +-\n sha1-name.c                            |   2 +-\n wt-status.c                            |   2 +-\n 36 files changed, 1006 insertions(+), 332 deletions(-)\n create mode 100644 Documentation/detach-head.txt\n create mode 100644 Documentation/git-restore-files.txt\n create mode 100644 Documentation/git-switch-branch.txt\n\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364356","messageId":"20181129215850.7278-2-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 01/14] git-checkout.txt: fix one syntax line","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:36Z","receivedAt":"2018-11-29T21:59:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"<branch> can be omitted in this syntax, and it's actually documented a\nfew paragraphs down:\n\n  You could omit <branch>, in which case the command degenerates to\n  \"check out the current branch\", which is a glorified no-op with\n  rather expensive side-effects to show only the tracking information,\n  if exists, for the current branch.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 801de2f764..65bd1bc50d 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -23,7 +23,7 @@ or the specified tree.  If no paths are given, 'git checkout' will\n also update `HEAD` to set the specified branch as the current\n branch.\n \n-'git checkout' <branch>::\n+'git checkout' [<branch>]::\n \tTo prepare for working on <branch>, switch to it by updating\n \tthe index and the files in the working tree, and by pointing\n \tHEAD at the branch. Local modifications to the files in the\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364357","messageId":"20181129215850.7278-4-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 03/14] checkout: factor out some code in parse_branchname_arg()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:38Z","receivedAt":"2018-11-29T21:59:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is in preparation for the new command restore-files, which also\nneeds to parse opts->source_tree but does not need all the\ndisambiguation logic.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 51 ++++++++++++++++++++++++++++------------------\n 1 file changed, 31 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex acdafc6e4c..1887c996c6 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -990,6 +990,34 @@ static int git_checkout_config(const char *var, const char *value, void *cb)\n \treturn git_xmerge_config(var, value, NULL);\n }\n \n+static void setup_new_branch_info_and_source_tree(\n+\tstruct branch_info *new_branch_info,\n+\tstruct checkout_opts *opts,\n+\tstruct object_id *rev,\n+\tconst char *arg)\n+{\n+\tstruct tree **source_tree = &opts->source_tree;\n+\tstruct object_id branch_rev;\n+\n+\tnew_branch_info->name = arg;\n+\tsetup_branch_path(new_branch_info);\n+\n+\tif (!check_refname_format(new_branch_info->path, 0) &&\n+\t    !read_ref(new_branch_info->path, &branch_rev))\n+\t\toidcpy(rev, &branch_rev);\n+\telse\n+\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\n+\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n+\tif (!new_branch_info->commit) {\n+\t\t/* not a commit */\n+\t\t*source_tree = parse_tree_indirect(rev);\n+\t} else {\n+\t\tparse_commit_or_die(new_branch_info->commit);\n+\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n+\t}\n+}\n+\n static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tint dwim_new_local_branch_ok,\n \t\t\t\tstruct branch_info *new_branch_info,\n@@ -997,10 +1025,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tstruct object_id *rev,\n \t\t\t\tint *dwim_remotes_matched)\n {\n-\tstruct tree **source_tree = &opts->source_tree;\n \tconst char **new_branch = &opts->new_branch;\n \tint argcount = 0;\n-\tstruct object_id branch_rev;\n \tconst char *arg;\n \tint dash_dash_pos;\n \tint has_dash_dash = 0;\n@@ -1114,26 +1140,11 @@ static int parse_branchname_arg(int argc, const char **argv,\n \targv++;\n \targc--;\n \n-\tnew_branch_info->name = arg;\n-\tsetup_branch_path(new_branch_info);\n-\n-\tif (!check_refname_format(new_branch_info->path, 0) &&\n-\t    !read_ref(new_branch_info->path, &branch_rev))\n-\t\toidcpy(rev, &branch_rev);\n-\telse\n-\t\tnew_branch_info->path = NULL; /* not an existing branch */\n+\tsetup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg);\n \n-\tnew_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);\n-\tif (!new_branch_info->commit) {\n-\t\t/* not a commit */\n-\t\t*source_tree = parse_tree_indirect(rev);\n-\t} else {\n-\t\tparse_commit_or_die(new_branch_info->commit);\n-\t\t*source_tree = get_commit_tree(new_branch_info->commit);\n-\t}\n-\n-\tif (!*source_tree)                   /* case (1): want a tree */\n+\tif (!opts->source_tree)                   /* case (1): want a tree */\n \t\tdie(_(\"reference is not a tree: %s\"), arg);\n+\n \tif (!has_dash_dash) {\t/* case (3).(d) -> (1) */\n \t\t/*\n \t\t * Do not complain the most common case\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364358","messageId":"20181129215850.7278-5-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 04/14] checkout: make \"opts\" in cmd_checkout() a pointer","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:39Z","receivedAt":"2018-11-29T21:59:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"opts\" will soon be moved out of cmd_checkout(). To keep changes in\nthat patch smaller, convert \"opts\" to a pointer and keep the real\nthing behind \"real_opts\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 109 +++++++++++++++++++++++----------------------\n 1 file changed, 55 insertions(+), 54 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1887c996c6..1b19328d0a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1236,76 +1236,77 @@ static int checkout_branch(struct checkout_opts *opts,\n \n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n-\tstruct checkout_opts opts;\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tchar *conflict_style = NULL;\n \tint dwim_new_local_branch = 1;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n-\t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n-\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n \t\t\t   N_(\"create/reset and checkout a branch\")),\n-\t\tOPT_BOOL('l', NULL, &opts.new_branch_log, N_(\"create reflog for new branch\")),\n-\t\tOPT_BOOL(0, \"detach\", &opts.force_detach, N_(\"detach HEAD at named commit\")),\n-\t\tOPT_SET_INT('t', \"track\",  &opts.track, N_(\"set upstream info for new branch\"),\n+\t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n+\t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n+\t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n-\t\tOPT_STRING(0, \"orphan\", &opts.new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n-\t\tOPT_SET_INT_F('2', \"ours\", &opts.writeout_stage,\n+\t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n-\t\tOPT_SET_INT_F('3', \"theirs\", &opts.writeout_stage,\n+\t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts.force, N_(\"force checkout (throw away local modifications)\"),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts.merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts.overwrite_ignore,\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n \t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n-\t\tOPT_BOOL('p', \"patch\", &opts.patch_mode, N_(\"select hunks interactively\")),\n-\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts.ignore_skipworktree,\n+\t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n \t\tOPT_HIDDEN_BOOL(0, \"guess\", &dwim_new_local_branch,\n \t\t\t\tN_(\"second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n \t\t\t    \"checkout\", \"control recursive updating of submodules\",\n \t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress reporting\")),\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_END(),\n \t};\n \n-\tmemset(&opts, 0, sizeof(opts));\n+\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n-\topts.overwrite_ignore = 1;\n-\topts.prefix = prefix;\n-\topts.show_progress = -1;\n+\topts->overwrite_ignore = 1;\n+\topts->prefix = prefix;\n+\topts->show_progress = -1;\n \n-\tgit_config(git_checkout_config, &opts);\n+\tgit_config(git_checkout_config, opts);\n \n-\topts.track = BRANCH_TRACK_UNSPECIFIED;\n+\topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n-\tif (opts.show_progress < 0) {\n-\t\tif (opts.quiet)\n-\t\t\topts.show_progress = 0;\n+\tif (opts->show_progress < 0) {\n+\t\tif (opts->quiet)\n+\t\t\topts->show_progress = 0;\n \t\telse\n-\t\t\topts.show_progress = isatty(2);\n+\t\t\topts->show_progress = isatty(2);\n \t}\n \n \tif (conflict_style) {\n-\t\topts.merge = 1; /* implied */\n+\t\topts->merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n \t}\n \n-\tif ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)\n+\tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n \t\tdie(_(\"-b, -B and --orphan are mutually exclusive\"));\n \n \t/*\n@@ -1313,14 +1314,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t * and new_branch_force and new_orphan_branch will tell us which one of\n \t * -b/-B/--orphan is being used.\n \t */\n-\tif (opts.new_branch_force)\n-\t\topts.new_branch = opts.new_branch_force;\n+\tif (opts->new_branch_force)\n+\t\topts->new_branch = opts->new_branch_force;\n \n-\tif (opts.new_orphan_branch)\n-\t\topts.new_branch = opts.new_orphan_branch;\n+\tif (opts->new_orphan_branch)\n+\t\topts->new_branch = opts->new_orphan_branch;\n \n \t/* --track without -b/-B/--orphan should DWIM */\n-\tif (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) {\n+\tif (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {\n \t\tconst char *argv0 = argv[0];\n \t\tif (!argc || !strcmp(argv0, \"--\"))\n \t\t\tdie(_(\"--track needs a branch name\"));\n@@ -1329,7 +1330,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\targv0 = strchr(argv0, '/');\n \t\tif (!argv0 || !argv0[1])\n \t\t\tdie(_(\"missing branch name; try -b\"));\n-\t\topts.new_branch = argv0 + 1;\n+\t\topts->new_branch = argv0 + 1;\n \t}\n \n \t/*\n@@ -1348,56 +1349,56 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tif (argc) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n-\t\t\t!opts.patch_mode &&\n+\t\t\t!opts->patch_mode &&\n \t\t\tdwim_new_local_branch &&\n-\t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n-\t\t\t!opts.new_branch;\n+\t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n+\t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &new_branch_info, opts, &rev,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\n \n \tif (argc) {\n-\t\tparse_pathspec(&opts.pathspec, 0,\n-\t\t\t       opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n+\t\tparse_pathspec(&opts->pathspec, 0,\n+\t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n \t\t\t       prefix, argv);\n \n-\t\tif (!opts.pathspec.nr)\n+\t\tif (!opts->pathspec.nr)\n \t\t\tdie(_(\"invalid path specification\"));\n \n \t\t/*\n \t\t * Try to give more helpful suggestion.\n \t\t * new_branch && argc > 1 will be caught later.\n \t\t */\n-\t\tif (opts.new_branch && argc == 1)\n+\t\tif (opts->new_branch && argc == 1)\n \t\t\tdie(_(\"'%s' is not a commit and a branch '%s' cannot be created from it\"),\n-\t\t\t\targv[0], opts.new_branch);\n+\t\t\t\targv[0], opts->new_branch);\n \n-\t\tif (opts.force_detach)\n+\t\tif (opts->force_detach)\n \t\t\tdie(_(\"git checkout: --detach does not take a path argument '%s'\"),\n \t\t\t    argv[0]);\n \n-\t\tif (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)\n+\t\tif (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)\n \t\t\tdie(_(\"git checkout: --ours/--theirs, --force and --merge are incompatible when\\n\"\n \t\t\t      \"checking out of the index.\"));\n \t}\n \n-\tif (opts.new_branch) {\n+\tif (opts->new_branch) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tif (opts.new_branch_force)\n-\t\t\topts.branch_exists = validate_branchname(opts.new_branch, &buf);\n+\t\tif (opts->new_branch_force)\n+\t\t\topts->branch_exists = validate_branchname(opts->new_branch, &buf);\n \t\telse\n-\t\t\topts.branch_exists =\n-\t\t\t\tvalidate_new_branchname(opts.new_branch, &buf, 0);\n+\t\t\topts->branch_exists =\n+\t\t\t\tvalidate_new_branchname(opts->new_branch, &buf, 0);\n \t\tstrbuf_release(&buf);\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr) {\n-\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\tif (opts->patch_mode || opts->pathspec.nr) {\n+\t\tint ret = checkout_paths(opts, new_branch_info.name);\n \t\tif (ret && dwim_remotes_matched > 1 &&\n \t\t    advice_checkout_ambiguous_remote_branch_name)\n \t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n@@ -1416,6 +1417,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n-\t\treturn checkout_branch(&opts, &new_branch_info);\n+\t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364359","messageId":"20181129215850.7278-6-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 05/14] checkout: move 'confict_style' and 'dwim_..' to checkout_opts","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:40Z","receivedAt":"2018-11-29T21:59:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These local variables are referenced by struct option[]. This struct\nwill soon be broken down, moved away and we can't rely on local\nvariables anymore. Move these two to struct checkout_opts in\npreparation for that.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 16 +++++++++-------\n 1 file changed, 9 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1b19328d0a..2423fdbf94 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -44,6 +44,8 @@ struct checkout_opts {\n \tint ignore_skipworktree;\n \tint ignore_other_worktrees;\n \tint show_progress;\n+\tint dwim_new_local_branch;\n+\n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n \t * should be updated accordingly.\n@@ -55,6 +57,7 @@ struct checkout_opts {\n \tint new_branch_log;\n \tenum branch_track track;\n \tstruct diff_options diff_options;\n+\tchar *conflict_style;\n \n \tint branch_exists;\n \tconst char *prefix;\n@@ -1239,8 +1242,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct checkout_opts real_opts;\n \tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n-\tchar *conflict_style = NULL;\n-\tint dwim_new_local_branch = 1;\n \tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n@@ -1265,12 +1266,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n \t\t\t   N_(\"update ignored files (default)\"),\n \t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &conflict_style, N_(\"style\"),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n \t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_HIDDEN_BOOL(0, \"guess\", &dwim_new_local_branch,\n+\t\tOPT_HIDDEN_BOOL(0, \"guess\", &opts->dwim_new_local_branch,\n \t\t\t\tN_(\"second guess 'git checkout <no-such-branch>'\")),\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n@@ -1286,6 +1287,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n+\topts->dwim_new_local_branch = 1;\n \n \tgit_config(git_checkout_config, opts);\n \n@@ -1301,9 +1303,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\topts->show_progress = isatty(2);\n \t}\n \n-\tif (conflict_style) {\n+\tif (opts->conflict_style) {\n \t\topts->merge = 1; /* implied */\n-\t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n+\t\tgit_xmerge_config(\"merge.conflictstyle\", opts->conflict_style, NULL);\n \t}\n \n \tif ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)\n@@ -1350,7 +1352,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n \t\t\t!opts->patch_mode &&\n-\t\t\tdwim_new_local_branch &&\n+\t\t\topts->dwim_new_local_branch &&\n \t\t\topts->track == BRANCH_TRACK_UNSPECIFIED &&\n \t\t\t!opts->new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364360","messageId":"20181129215850.7278-7-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 06/14] checkout: split options[] array in three pieces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:41Z","receivedAt":"2018-11-29T21:59:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a preparation step for introducing new commands that do parts\nof what checkout does. There will be two new commands, one is about\nswitching branches, detaching HEAD... one about checking out\npaths. These share the a subset of command line options. The rest of\ncommand line options are separate.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 77 +++++++++++++++++++++++++++++++++-------------\n parse-options-cb.c | 16 ++++++++++\n parse-options.h    |  3 +-\n 3 files changed, 73 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2423fdbf94..764e1a83a1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1237,14 +1237,31 @@ static int checkout_branch(struct checkout_opts *opts,\n \treturn switch_branches(opts, new_branch_info);\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static struct option *add_common_options(struct checkout_opts *opts,\n+\t\t\t\t\t struct option *prevopts)\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n-\tstruct branch_info new_branch_info;\n-\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts->quiet, N_(\"suppress progress reporting\")),\n+\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n+\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n+\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n+\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n+\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n+\t\t\t   PARSE_OPT_NOCOMPLETE),\n+\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n+\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n+\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_switch_branch_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n \t\t\t   N_(\"create and checkout a new branch\")),\n \t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n@@ -1254,33 +1271,44 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_STRING(0, \"orphan\", &opts->new_orphan_branch, N_(\"new-branch\"), N_(\"new unparented branch\")),\n+\t\tOPT_HIDDEN_BOOL(0, \"guess\", &opts->dwim_new_local_branch,\n+\t\t\t\tN_(\"second guess 'git checkout <no-such-branch>'\")),\n+\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n+\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n+\t\tOPT_END()\n+\t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+static struct option *add_checkout_path_options(struct checkout_opts *opts,\n+\t\t\t\t\t\tstruct option *prevopts)\n+{\n+\tstruct option options[] = {\n \t\tOPT_SET_INT_F('2', \"ours\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout our version for unmerged files\"),\n \t\t\t      2, PARSE_OPT_NONEG),\n \t\tOPT_SET_INT_F('3', \"theirs\", &opts->writeout_stage,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n-\t\tOPT__FORCE(&opts->force, N_(\"force checkout (throw away local modifications)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n-\t\tOPT_BOOL_F(0, \"overwrite-ignore\", &opts->overwrite_ignore,\n-\t\t\t   N_(\"update ignored files (default)\"),\n-\t\t\t   PARSE_OPT_NOCOMPLETE),\n-\t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n \t\t\t N_(\"do not limit pathspecs to sparse entries only\")),\n-\t\tOPT_HIDDEN_BOOL(0, \"guess\", &opts->dwim_new_local_branch,\n-\t\t\t\tN_(\"second guess 'git checkout <no-such-branch>'\")),\n-\t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts->ignore_other_worktrees,\n-\t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n-\t\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", NULL,\n-\t\t\t    \"checkout\", \"control recursive updating of submodules\",\n-\t\t\t    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },\n-\t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n-\t\tOPT_END(),\n+\t\tOPT_END()\n \t};\n+\tstruct option *newopts = parse_options_concat(prevopts, options);\n+\tfree(prevopts);\n+\treturn newopts;\n+}\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts real_opts;\n+\tstruct checkout_opts *opts = &real_opts;\n+\tstruct branch_info new_branch_info;\n+\tint dwim_remotes_matched = 0;\n+\tstruct option *options = NULL;\n \n \tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n@@ -1293,6 +1321,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(opts, options);\n+\toptions = add_switch_branch_options(opts, options);\n+\toptions = add_checkout_path_options(opts, options);\n+\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex 8c9edce52f..f46b3cb0a4 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -121,6 +121,22 @@ int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+struct option *parse_options_dup(const struct option *o)\n+{\n+\tstruct option *opts;\n+\tint nr = 0;\n+\n+\twhile (o && o->type != OPTION_END) {\n+\t\tnr++;\n+\t\to++;\n+\t}\n+\n+\tCALLOC_ARRAY(opts, nr + 1);\n+\tmemcpy(opts, o - nr, sizeof(*o) * nr);\n+\topts[nr].type = OPTION_END;\n+\treturn opts;\n+}\n+\n struct option *parse_options_concat(struct option *a, struct option *b)\n {\n \tstruct option *ret;\ndiff --git a/parse-options.h b/parse-options.h\nindex 6c4fe2016d..584cb521f5 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -239,7 +239,8 @@ extern int parse_options_step(struct parse_opt_ctx_t *ctx,\n \n extern int parse_options_end(struct parse_opt_ctx_t *ctx);\n \n-extern struct option *parse_options_concat(struct option *a, struct option *b);\n+struct option *parse_options_dup(const struct option *a);\n+struct option *parse_options_concat(struct option *a, struct option *b);\n \n /*----- some often used options -----*/\n extern int parse_opt_abbrev_cb(const struct option *, const char *, int);\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364361","messageId":"20181129215850.7278-9-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 08/14] switch-branch: better names for -b and -B","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:43Z","receivedAt":"2018-11-29T21:59:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The shortcut of these options do not make much sense when used with\nswitch-branch. And their descriptions are also tied to checkout\nout. Move -b/-B to cmd_checkout() and new -c/-C with the same\nfunctionality in cmd_switch_branch()\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 30 +++++++++++++++++++-----------\n 1 file changed, 19 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 7dc0f4d3f3..ceb635de36 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1268,14 +1268,10 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-static struct option *add_switch_branch_options(struct checkout_opts *opts,\n-\t\t\t\t\t\tstruct option *prevopts)\n+static struct option *add_common_switch_branch_options(\n+\tstruct checkout_opts *opts, struct option *prevopts)\n {\n \tstruct option options[] = {\n-\t\tOPT_STRING('b', NULL, &opts->new_branch, N_(\"branch\"),\n-\t\t\t   N_(\"create and checkout a new branch\")),\n-\t\tOPT_STRING('B', NULL, &opts->new_branch_force, N_(\"branch\"),\n-\t\t\t   N_(\"create/reset and checkout a branch\")),\n \t\tOPT_BOOL('l', NULL, &opts->new_branch_log, N_(\"create reflog for new branch\")),\n \t\tOPT_BOOL(0, \"detach\", &opts->force_detach, N_(\"detach HEAD at named commit\")),\n \t\tOPT_SET_INT('t', \"track\",  &opts->track, N_(\"set upstream info for new branch\"),\n@@ -1461,15 +1457,21 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n-\tstruct option *options = NULL;\n+\tstruct option *options;\n+\tstruct option checkout_options[] = {\n+\t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and checkout a new branch\")),\n+\t\tOPT_STRING('B', NULL, &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and checkout a branch\")),\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \toptions = add_checkout_path_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n@@ -1482,14 +1484,20 @@ int cmd_switch_branch(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n \tstruct option *options = NULL;\n+\tstruct option switch_options[] = {\n+\t\tOPT_STRING('c', \"create\", &opts.new_branch, N_(\"branch\"),\n+\t\t\t   N_(\"create and switch to a new branch\")),\n+\t\tOPT_STRING('C', \"force-create\", &opts.new_branch_force, N_(\"branch\"),\n+\t\t\t   N_(\"create/reset and switch to a new branch\")),\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_common_switch_branch_options(&opts, options);\n \n \tret = checkout_main(argc, argv, prefix, &opts,\n \t\t\t    options, switch_branch_usage);\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364362","messageId":"20181129215850.7278-10-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 09/14] switch-branch: stop accepting pathspec","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:44Z","receivedAt":"2018-11-29T21:59:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This command is about switching branch (or creating a new one) and\nshould not accept pathspec. This helps simplify ambiguation\nhandling. The other two (\"git checkout\" and \"git restore-files\") of\ncourse do accept pathspec as before.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 14 ++++++++++++--\n 1 file changed, 12 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex ceb635de36..880030e929 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -55,6 +55,7 @@ struct checkout_opts {\n \tint ignore_other_worktrees;\n \tint show_progress;\n \tint dwim_new_local_branch;\n+\tint accept_pathspec;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1089,10 +1090,16 @@ static int parse_branchname_arg(int argc, const char **argv,\n \tif (!argc)\n \t\treturn 0;\n \n+\tif (!opts->accept_pathspec) {\n+\t\tif (argc > 1)\n+\t\t\tdie(_(\"only one reference expected\"));\n+\t\thas_dash_dash = 1; /* helps disambiguate */\n+\t}\n+\n \targ = argv[0];\n \tdash_dash_pos = -1;\n \tfor (i = 0; i < argc; i++) {\n-\t\tif (!strcmp(argv[i], \"--\")) {\n+\t\tif (opts->accept_pathspec && !strcmp(argv[i], \"--\")) {\n \t\t\tdash_dash_pos = i;\n \t\t\tbreak;\n \t\t}\n@@ -1167,7 +1174,7 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t */\n \t\tif (argc)\n \t\t\tverify_non_filename(opts->prefix, arg);\n-\t} else {\n+\t} else if (opts->accept_pathspec) {\n \t\targcount++;\n \t\targv++;\n \t\targc--;\n@@ -1468,6 +1475,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n+\topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1494,6 +1502,7 @@ int cmd_switch_branch(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n+\topts.accept_pathspec = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n@@ -1513,6 +1522,7 @@ int cmd_restore_files(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n+\topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(options);\n \toptions = add_common_options(&opts, options);\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364363","messageId":"20181129215850.7278-11-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 10/14] switch-branch: reject \"do nothing\" case","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:45Z","receivedAt":"2018-11-29T21:59:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" can be executed without any arguments. What it does is\nnot exactly great: it switches from HEAD to HEAD and showing worktree\nmodification as a side effect.\n\nMake switch-branch reject this case. You have to either\n\n- really switch a branch\n- (explicitly) detach from the current branch\n- create a new branch\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 10 ++++++++++\n 1 file changed, 10 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 880030e929..c7ae068d2c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -56,6 +56,7 @@ struct checkout_opts {\n \tint show_progress;\n \tint dwim_new_local_branch;\n \tint accept_pathspec;\n+\tint switch_branch_doing_nothing_not_ok;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1233,6 +1234,13 @@ static int checkout_branch(struct checkout_opts *opts,\n \t\tdie(_(\"Cannot switch branch to a non-commit '%s'\"),\n \t\t    new_branch_info->name);\n \n+\tif (opts->switch_branch_doing_nothing_not_ok &&\n+\t    !new_branch_info->name &&\n+\t    !opts->new_branch &&\n+\t    !opts->new_branch_force &&\n+\t    !opts->force_detach)\n+\t\tdie(_(\"nothing to do\"));\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1475,6 +1483,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n+\topts.switch_branch_doing_nothing_not_ok = 0;\n \topts.accept_pathspec = 1;\n \n \toptions = parse_options_dup(checkout_options);\n@@ -1503,6 +1512,7 @@ int cmd_switch_branch(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n \topts.accept_pathspec = 0;\n+\topts.switch_branch_doing_nothing_not_ok = 1;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364364","messageId":"20181129215850.7278-12-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 11/14] switch-branch: only allow explicit detached HEAD","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:46Z","receivedAt":"2018-11-29T21:59:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout <commit>\" will checkout the commit in question and\ndetach HEAD from the current branch. It is naturally a right thing to\ndo once you get git references. But detached HEAD is a scary concept\nto new users because we show a lot of warnings and stuff, and it could\nbe hard to get out of (until you know better).\n\nTo keep switch-branch a bit more friendly to new users, we only allow\nentering detached HEAD mode when --detach is given. \"git\nswitch-branch\" must take a branch (unless you create a new branch,\nthen of course switch-branch can take any commit-ish)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 10 ++++++++++\n 1 file changed, 10 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex c7ae068d2c..fbfebba2d9 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -49,6 +49,7 @@ struct checkout_opts {\n \tint merge;\n \tint force;\n \tint force_detach;\n+\tint implicit_detach;\n \tint writeout_stage;\n \tint overwrite_ignore;\n \tint ignore_skipworktree;\n@@ -1241,6 +1242,13 @@ static int checkout_branch(struct checkout_opts *opts,\n \t    !opts->force_detach)\n \t\tdie(_(\"nothing to do\"));\n \n+\tif (!opts->implicit_detach &&\n+\t    !opts->new_branch &&\n+\t    !opts->new_branch_force &&\n+\t    new_branch_info->name &&\n+\t    !new_branch_info->path)\n+\t\tdie(_(\"a branch is expected, got %s\"), new_branch_info->name);\n+\n \tif (new_branch_info->path && !opts->force_detach && !opts->new_branch &&\n \t    !opts->ignore_other_worktrees) {\n \t\tint flag;\n@@ -1485,6 +1493,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_not_ok = 0;\n \topts.accept_pathspec = 1;\n+\topts.implicit_detach = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1513,6 +1522,7 @@ int cmd_switch_branch(int argc, const char **argv, const char *prefix)\n \topts.dwim_new_local_branch = 1;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_not_ok = 1;\n+\topts.implicit_detach = 0;\n \n \toptions = parse_options_dup(switch_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364365","messageId":"20181129215850.7278-13-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 12/14] restore-files: take tree-ish from --from option instead","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:47Z","receivedAt":"2018-11-29T21:59:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is another departure from 'git checkout' syntax, which uses -- to\nseparate ref and pathspec. The observation is restore-files (or \"git\ncheckout ,, <pathspec>\") is most often used to restore some files from\nthe index. If this is correct, we can simplify it by taking a way the\nref, so that we can write\n\n    git restore-files some-file\n\nwithout worrying about some-file being a ref and whether we need to do\n\n    git restore-files -- some-file\n\nfor safety. If the source of the restore comes from a tree, it will be\nin the form of an option with value, e.g.\n\n    git restore-files --from=this-tree some-file\n\nThis is of course longer to type than using \"--\". But hopefully it\nwill not be used as often, and it is clearly easier to understand.\n\ndwim_new_local_branch is no longer set (or unset) in cmd_restore_files()\nbecause it's irrelevant because we don't really care about dwim-ing.\nWith accept_ref being unset, dwim can't happen.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 41 ++++++++++++++++++++++++++++++++++-------\n 1 file changed, 34 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex fbfebba2d9..7ff9951818 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -39,7 +39,7 @@ static const char * const switch_branch_usage[] = {\n };\n \n static const char * const restore_files_usage[] = {\n-\tN_(\"git restore-files [<options>] [<branch>] -- <file>...\"),\n+\tN_(\"git restore-files [<options>] [--from=<branch>] <file>...\"),\n \tNULL,\n };\n \n@@ -56,6 +56,7 @@ struct checkout_opts {\n \tint ignore_other_worktrees;\n \tint show_progress;\n \tint dwim_new_local_branch;\n+\tint accept_ref;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_not_ok;\n \n@@ -75,6 +76,7 @@ struct checkout_opts {\n \tint branch_exists;\n \tconst char *prefix;\n \tstruct pathspec pathspec;\n+\tconst char *from_treeish;\n \tstruct tree *source_tree;\n };\n \n@@ -1337,6 +1339,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n {\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n+\tint parseopt_flags = 0;\n \n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n@@ -1347,8 +1350,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\targc = parse_options(argc, argv, prefix, options, usagestr,\n-\t\t\t     PARSE_OPT_KEEP_DASHDASH);\n+\tif (!opts->accept_pathspec && !opts->accept_ref)\n+\t\tBUG(\"make up your mind, you need to take _something_\");\n+\tif (opts->accept_pathspec && opts->accept_ref)\n+\t\tparseopt_flags = PARSE_OPT_KEEP_DASHDASH;\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     usagestr, parseopt_flags);\n \n \tif (opts->show_progress < 0) {\n \t\tif (opts->quiet)\n@@ -1402,7 +1410,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t * including \"last branch\" syntax and DWIM-ery for names of\n \t * remote branches, erroring out for invalid or ambiguous cases.\n \t */\n-\tif (argc) {\n+\tif (argc && opts->accept_ref && opts->accept_pathspec) {\n \t\tstruct object_id rev;\n \t\tint dwim_ok =\n \t\t\t!opts->patch_mode &&\n@@ -1414,6 +1422,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n+\t} else if (!opts->accept_ref && opts->from_treeish) {\n+\t\tstruct object_id rev;\n+\n+\t\tif (get_oid_mb(opts->from_treeish, &rev))\n+\t\t\tdie(_(\"could not resolve %s\"), opts->from_treeish);\n+\n+\t\tsetup_new_branch_info_and_source_tree(&new_branch_info,\n+\t\t\t\t\t\t      opts, &rev,\n+\t\t\t\t\t\t      opts->from_treeish);\n+\n+\t\tif (!opts->source_tree)\n+\t\t\tdie(_(\"reference is not a tree: %s\"), opts->from_treeish);\n \t}\n \n \tif (argc) {\n@@ -1492,6 +1512,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n \topts.switch_branch_doing_nothing_not_ok = 0;\n+\topts.accept_ref = 1;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n \n@@ -1520,6 +1541,7 @@ int cmd_switch_branch(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.dwim_new_local_branch = 1;\n+\topts.accept_ref = 1;\n \topts.accept_pathspec = 0;\n \topts.switch_branch_doing_nothing_not_ok = 1;\n \topts.implicit_detach = 0;\n@@ -1537,14 +1559,19 @@ int cmd_switch_branch(int argc, const char **argv, const char *prefix)\n int cmd_restore_files(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n-\tstruct option *options = NULL;\n+\tstruct option *options;\n+\tstruct option restore_options[] = {\n+\t\tOPT_STRING(0, \"from\", &opts.from_treeish, \"<tree-ish>\",\n+\t\t\t   N_(\"where the checkout from\")),\n+\t\tOPT_END()\n+\t};\n \tint ret;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.dwim_new_local_branch = 1;\n+\topts.accept_ref = 0;\n \topts.accept_pathspec = 1;\n \n-\toptions = parse_options_dup(options);\n+\toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n \toptions = add_checkout_path_options(&opts, options);\n \n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364366","messageId":"20181129215850.7278-14-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 13/14] restore-files: make pathspec mandatory","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:48Z","receivedAt":"2018-11-29T21:59:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git restore-files\" without arguments does not make much sense when\nit's about restoring files (what files now?). We could default to\neither\n\n    git restore-files .\n\nor\n\n    git restore-files :/\n\nNeither is intuitive. Make the user always give pathspec, force the\nuser to think the scope of restore they want.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 6 ++++++\n 1 file changed, 6 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 7ff9951818..961a90b1c0 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -59,6 +59,7 @@ struct checkout_opts {\n \tint accept_ref;\n \tint accept_pathspec;\n \tint switch_branch_doing_nothing_not_ok;\n+\tint empty_pathspec_ok;\n \n \t/*\n \t * If new checkout options are added, skip_merge_working_tree\n@@ -1436,6 +1437,9 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"reference is not a tree: %s\"), opts->from_treeish);\n \t}\n \n+\tif (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc)\n+\t\tdie(_(\"pathspec is required\"));\n+\n \tif (argc) {\n \t\tparse_pathspec(&opts->pathspec, 0,\n \t\t\t       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,\n@@ -1515,6 +1519,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \topts.accept_ref = 1;\n \topts.accept_pathspec = 1;\n \topts.implicit_detach = 1;\n+\topts.empty_pathspec_ok = 1;\n \n \toptions = parse_options_dup(checkout_options);\n \toptions = add_common_options(&opts, options);\n@@ -1570,6 +1575,7 @@ int cmd_restore_files(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \topts.accept_ref = 0;\n \topts.accept_pathspec = 1;\n+\topts.empty_pathspec_ok = 0;\n \n \toptions = parse_options_dup(restore_options);\n \toptions = add_common_options(&opts, options);\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364367","messageId":"20181129215850.7278-15-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 14/14] doc: promote \"git switch-branch\" and \"git restore-files\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:49Z","receivedAt":"2018-11-29T21:59:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The two new commands \"git switch-branch\" and \"git restore-files\" are\nadded to avoid the confusion of one-command-do-all \"git checkout\" for\nnew users. They are also helpful to avoid ambiguation context.\n\nFor these reasons, promote them everywhere possible. This includes\ndocumentation, suggestions/advice from other commands...\n\n\"git checkout\" is also removed from \"git help\" (i.e. it's no longer\nconsidered a commonly used command)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config/advice.txt        | 10 +++--\n Documentation/config/checkout.txt      |  5 ++-\n Documentation/git-branch.txt           |  8 ++--\n Documentation/git-check-ref-format.txt |  2 +-\n Documentation/git-format-patch.txt     |  2 +-\n Documentation/git-merge-base.txt       |  2 +-\n Documentation/git-rebase.txt           |  2 +-\n Documentation/git-remote.txt           |  2 +-\n Documentation/git-rerere.txt           | 10 ++---\n Documentation/git-reset.txt            | 18 ++++-----\n Documentation/git-revert.txt           |  2 +-\n Documentation/git-stash.txt            |  6 +--\n Documentation/gitattributes.txt        |  2 +-\n Documentation/gitcli.txt               |  4 +-\n Documentation/gitcore-tutorial.txt     | 18 ++++-----\n Documentation/giteveryday.txt          | 24 ++++++------\n Documentation/githooks.txt             |  5 ++-\n Documentation/gittutorial-2.txt        |  2 +-\n Documentation/gittutorial.txt          |  4 +-\n Documentation/revisions.txt            |  2 +-\n Documentation/user-manual.txt          | 54 +++++++++++++-------------\n advice.c                               | 11 ++++--\n command-list.txt                       |  2 +-\n sha1-name.c                            |  2 +-\n wt-status.c                            |  2 +-\n 25 files changed, 104 insertions(+), 97 deletions(-)\n\ndiff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt\nindex 57fcd4c862..bffc503385 100644\n--- a/Documentation/config/advice.txt\n+++ b/Documentation/config/advice.txt\n@@ -35,7 +35,8 @@ advice.*::\n \t\tstate in the output of linkgit:git-status[1], in\n \t\tthe template shown when writing commit messages in\n \t\tlinkgit:git-commit[1], and in the help message shown\n-\t\tby linkgit:git-checkout[1] when switching branch.\n+\t\tby linkgit:git-switch-branch[1] or\n+\t\tlinkgit:git-checkout[1] when switching branch.\n \tstatusUoption::\n \t\tAdvise to consider using the `-u` option to linkgit:git-status[1]\n \t\twhen the command takes more than 2 seconds to enumerate untracked\n@@ -55,9 +56,10 @@ advice.*::\n \t\tyour information is guessed from the system username and\n \t\tdomain name.\n \tdetachedHead::\n-\t\tAdvice shown when you used linkgit:git-checkout[1] to\n-\t\tmove to the detach HEAD state, to instruct how to create\n-\t\ta local branch after the fact.\n+\t\tAdvice shown when you used\n+\t\tlinkgit:git-switch-branch[1] or linkgit:git-checkout[1]\n+\t\tto move to the detach HEAD state, to instruct how to\n+\t\tcreate a local branch after the fact.\n \tcheckoutAmbiguousRemoteBranchName::\n \t\tAdvice shown when the argument to\n \t\tlinkgit:git-checkout[1] ambiguously resolves to a\ndiff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt\nindex c4118fa196..81b0d47ced 100644\n--- a/Documentation/config/checkout.txt\n+++ b/Documentation/config/checkout.txt\n@@ -8,8 +8,9 @@ checkout.defaultRemote::\n \tdisambiguation. The typical use-case is to set this to\n \t`origin`.\n +\n-Currently this is used by linkgit:git-checkout[1] when 'git checkout\n-<something>' will checkout the '<something>' branch on another remote,\n+Currently this is used by linkgit:git-switch-branch[1] and\n+linkgit:git-checkout[1] when 'git checkout <something>'\n+will checkout the '<something>' branch on another remote,\n and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n remote branch. This setting might be used for other checkout-like\n commands or functionality in the future.\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex bf5316ffa9..1564df47d2 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -48,7 +48,7 @@ The command's second form creates a new branch head named <branchname>\n which points to the current `HEAD`, or <start-point> if given.\n \n Note that this will create the new branch, but it will not switch the\n-working tree to it; use \"git checkout <newbranch>\" to switch to the\n+working tree to it; use \"git switch-branch <newbranch>\" to switch to the\n new branch.\n \n When a local branch is started off a remote-tracking branch, Git sets up the\n@@ -194,7 +194,7 @@ This option is only applicable in non-verbose mode.\n +\n This behavior is the default when the start point is a remote-tracking branch.\n Set the branch.autoSetupMerge configuration variable to `false` if you\n-want `git checkout` and `git branch` to always behave as if `--no-track`\n+want `git switch-branch` and `git branch` to always behave as if `--no-track`\n were given. Set it to `always` if you want this behavior when the\n start-point is either a local or remote-tracking branch.\n \n@@ -293,7 +293,7 @@ Start development from a known tag::\n $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6\n $ cd my2.6\n $ git branch my2.6.14 v2.6.14   <1>\n-$ git checkout my2.6.14\n+$ git switch-branch my2.6.14\n ------------\n +\n <1> This step and the next one could be combined into a single step with\n@@ -319,7 +319,7 @@ NOTES\n -----\n \n If you are creating a branch that you want to checkout immediately, it is\n-easier to use the git checkout command with its `-b` option to create\n+easier to use the \"git switch-branch\" command with its `-b` option to create\n a branch and check it out with a single command.\n \n The options `--contains`, `--no-contains`, `--merged` and `--no-merged`\ndiff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt\nindex d9de992585..38c2169d7a 100644\n--- a/Documentation/git-check-ref-format.txt\n+++ b/Documentation/git-check-ref-format.txt\n@@ -88,7 +88,7 @@ but it is explicitly forbidden at the beginning of a branch name).\n When run with `--branch` option in a repository, the input is first\n expanded for the ``previous checkout syntax''\n `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that\n-was checked out using \"git checkout\" operation. This option should be\n+was checked out using \"git switch-branch\" operation. This option should be\n used by porcelains to accept this syntax anywhere a branch name is\n expected, so they can act as if you typed the branch name. As an\n exception note that, the ``previous checkout operation'' might result\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex aba4c5febe..0ceaa1173c 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -416,7 +416,7 @@ One way to test if your MUA is set up correctly is:\n * Apply it:\n \n     $ git fetch <project> master:test-apply\n-    $ git checkout test-apply\n+    $ git switch-branch test-apply\n     $ git reset --hard\n     $ git am a.patch\n \ndiff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt\nindex 9f07f4f6ed..1b25e5d530 100644\n--- a/Documentation/git-merge-base.txt\n+++ b/Documentation/git-merge-base.txt\n@@ -149,7 +149,7 @@ instead.\n Discussion on fork-point mode\n -----------------------------\n \n-After working on the `topic` branch created with `git checkout -b\n+After working on the `topic` branch created with `git switch-branch -b\n topic origin/master`, the history of remote-tracking branch\n `origin/master` may have been rewound and rebuilt, leading to a\n history of this shape:\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 80793bad8d..fe10880633 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -17,7 +17,7 @@ SYNOPSIS\n DESCRIPTION\n -----------\n If <branch> is specified, 'git rebase' will perform an automatic\n-`git checkout <branch>` before doing anything else.  Otherwise\n+`git switch-branch <branch>` before doing anything else.  Otherwise\n it remains on the current branch.\n \n If <upstream> is not specified, the upstream configured in\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 0cad37fb81..044bbdb27c 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -230,7 +230,7 @@ $ git branch -r\n   staging/master\n   staging/staging-linus\n   staging/staging-next\n-$ git checkout -b staging staging/master\n+$ git switch-branch -b staging staging/master\n ...\n ------------\n \ndiff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt\nindex df310d2a58..fe9d21b395 100644\n--- a/Documentation/git-rerere.txt\n+++ b/Documentation/git-rerere.txt\n@@ -91,7 +91,7 @@ For such a test, you need to merge master and topic somehow.\n One way to do it is to pull master into the topic branch:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch-branch topic\n \t$ git merge master\n \n               o---*---o---+ topic\n@@ -113,10 +113,10 @@ the upstream might have been advanced since the test merge `+`,\n in which case the final commit graph would look like this:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch-branch topic\n \t$ git merge master\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch-branch master\n \t$ git merge topic\n \n               o---*---o---+---o---o topic\n@@ -136,11 +136,11 @@ merges, you could blow away the test merge, and keep building on\n top of the tip before the test merge:\n \n ------------\n-\t$ git checkout topic\n+\t$ git switch-branch topic\n \t$ git merge master\n \t$ git reset --hard HEAD^ ;# rewind the test merge\n \t$ ... work on both topic and master branches\n-\t$ git checkout master\n+\t$ git switch-branch master\n \t$ git merge topic\n \n               o---*---o-------o---o topic\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex 2dac95c71a..ca46b4c967 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -149,9 +149,9 @@ See also the --amend option to linkgit:git-commit[1].\n Undo a commit, making it a topic branch::\n +\n ------------\n-$ git branch topic/wip     <1>\n-$ git reset --hard HEAD~3  <2>\n-$ git checkout topic/wip   <3>\n+$ git branch topic/wip          <1>\n+$ git reset --hard HEAD~3       <2>\n+$ git switch-branch topic/wip   <3>\n ------------\n +\n <1> You have made some commits, but realize they were premature\n@@ -232,13 +232,13 @@ working tree are not in any shape to be committed yet, but you\n need to get to the other branch for a quick bugfix.\n +\n ------------\n-$ git checkout feature ;# you were working in \"feature\" branch and\n+$ git switch-branch feature ;# you were working in \"feature\" branch and\n $ work work work       ;# got interrupted\n $ git commit -a -m \"snapshot WIP\"                 <1>\n-$ git checkout master\n+$ git switch-branch master\n $ fix fix fix\n $ git commit ;# commit with real log\n-$ git checkout feature\n+$ git switch-branch feature\n $ git reset --soft HEAD^ ;# go back to WIP state  <2>\n $ git reset                                       <3>\n ------------\n@@ -279,18 +279,18 @@ reset it while keeping the changes in your working tree.\n +\n ------------\n $ git tag start\n-$ git checkout -b branch1\n+$ git switch-branch -b branch1\n $ edit\n $ git commit ...                            <1>\n $ edit\n-$ git checkout -b branch2                   <2>\n+$ git switch-branch -b branch2              <2>\n $ git reset --keep start                    <3>\n ------------\n +\n <1> This commits your first edits in branch1.\n <2> In the ideal world, you could have realized that the earlier\n     commit did not belong to the new topic when you created and switched\n-    to branch2 (i.e. \"git checkout -b branch2 start\"), but nobody is\n+    to branch2 (i.e. \"git switch-branch -b branch2 start\"), but nobody is\n     perfect.\n <3> But you can use \"reset --keep\" to remove the unwanted commit after\n     you switched to \"branch2\".\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 837707a8fd..07ef83866b 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -26,7 +26,7 @@ effect of some earlier commits (often only a faulty one).  If you want to\n throw away all uncommitted changes in your working directory, you\n should see linkgit:git-reset[1], particularly the `--hard` option.  If\n you want to extract specific files as they were in another commit, you\n-should see linkgit:git-checkout[1], specifically the `git checkout\n+should see linkgit:git-checkout[1], specifically the `git restore-files\n <commit> -- <filename>` syntax.  Take care with these alternatives as\n both will discard uncommitted changes in your working directory.\n \ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 7ef8c47911..ea226979b1 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -235,12 +235,12 @@ return to your original branch to make the emergency fix, like this:\n +\n ----------------------------------------------------------------\n # ... hack hack hack ...\n-$ git checkout -b my_wip\n+$ git switch-branch -b my_wip\n $ git commit -a -m \"WIP\"\n-$ git checkout master\n+$ git switch-branch master\n $ edit emergency fix\n $ git commit -a -m \"Fix in a hurry\"\n-$ git checkout my_wip\n+$ git switch-branch my_wip\n $ git reset --soft HEAD^\n # ... continue hacking ...\n ----------------------------------------------------------------\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex b8392fc330..df62bd8019 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -112,7 +112,7 @@ Checking-out and checking-in\n \n These attributes affect how the contents stored in the\n repository are copied to the working tree files when commands\n-such as 'git checkout' and 'git merge' run.  They also affect how\n+such as 'git switch-branch' and 'git merge' run.  They also affect how\n Git stores the contents you prepare in the working tree in the\n repository upon 'git add' and 'git commit'.\n \ndiff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt\nindex 592e06d839..491eb91c2e 100644\n--- a/Documentation/gitcli.txt\n+++ b/Documentation/gitcli.txt\n@@ -47,8 +47,8 @@ disambiguating `--` at appropriate places.\n    things:\n +\n --------------------------------\n-$ git checkout -- *.c\n-$ git checkout -- \\*.c\n+$ git restore-files -- *.c\n+$ git restore-files -- \\*.c\n --------------------------------\n +\n The former lets your shell expand the fileglob, and you are asking\ndiff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt\nindex e29a9effcc..49a8b5aa52 100644\n--- a/Documentation/gitcore-tutorial.txt\n+++ b/Documentation/gitcore-tutorial.txt\n@@ -741,7 +741,7 @@ used earlier, and create a branch in it. You do that by simply just\n saying that you want to check out a new branch:\n \n ------------\n-$ git checkout -b mybranch\n+$ git branch mybranch\n ------------\n \n will create a new branch based at the current `HEAD` position, and switch\n@@ -755,7 +755,7 @@ just telling 'git checkout' what the base of the checkout would be.\n In other words, if you have an earlier tag or branch, you'd just do\n \n ------------\n-$ git checkout -b mybranch earlier-commit\n+$ git switch-branch -b mybranch earlier-commit\n ------------\n \n and it would create the new branch `mybranch` at the earlier commit,\n@@ -765,7 +765,7 @@ and check out the state at that time.\n You can always just jump back to your original `master` branch by doing\n \n ------------\n-$ git checkout master\n+$ git switch-branch master\n ------------\n \n (or any other branch-name, for that matter) and if you forget which\n@@ -794,7 +794,7 @@ $ git branch <branchname> [startingpoint]\n \n which will simply _create_ the branch, but will not do anything further.\n You can then later -- once you decide that you want to actually develop\n-on that branch -- switch to that branch with a regular 'git checkout'\n+on that branch -- switch to that branch with a regular 'git switch-branch\n with the branchname as the argument.\n \n \n@@ -808,7 +808,7 @@ being the same as the original `master` branch, let's make sure we're in\n that branch, and do some work there.\n \n ------------------------------------------------\n-$ git checkout mybranch\n+$ git switch-branch mybranch\n $ echo \"Work, work, work\" >>hello\n $ git commit -m \"Some work.\" -i hello\n ------------------------------------------------\n@@ -825,7 +825,7 @@ does some work in the original branch, and simulate that by going back\n to the master branch, and editing the same file differently there:\n \n ------------\n-$ git checkout master\n+$ git switch-branch master\n ------------\n \n Here, take a moment to look at the contents of `hello`, and notice how they\n@@ -958,7 +958,7 @@ to the `master` branch. Let's go back to `mybranch`, and run\n 'git merge' to get the \"upstream changes\" back to your branch.\n \n ------------\n-$ git checkout mybranch\n+$ git switch-branch mybranch\n $ git merge -m \"Merge upstream changes.\" master\n ------------\n \n@@ -1133,9 +1133,9 @@ Remember, before running 'git merge', our `master` head was at\n work.\" commit.\n \n ------------\n-$ git checkout mybranch\n+$ git switch-branch mybranch\n $ git reset --hard master^2\n-$ git checkout master\n+$ git switch-branch master\n $ git reset --hard master^\n ------------\n \ndiff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt\nindex 9f2528fc8c..9d64544bb9 100644\n--- a/Documentation/giteveryday.txt\n+++ b/Documentation/giteveryday.txt\n@@ -80,9 +80,9 @@ $ git tag v2.43 <2>\n Create a topic branch and develop.::\n +\n ------------\n-$ git checkout -b alsa-audio <1>\n+$ git branch alsa-audio <1>\n $ edit/compile/test\n-$ git checkout -- curses/ux_audio_oss.c <2>\n+$ git restore-files -- curses/ux_audio_oss.c <2>\n $ git add curses/ux_audio_alsa.c <3>\n $ edit/compile/test\n $ git diff HEAD <4>\n@@ -90,7 +90,7 @@ $ git commit -a -s <5>\n $ edit/compile/test\n $ git diff HEAD^ <6>\n $ git commit -a --amend <7>\n-$ git checkout master <8>\n+$ git switch-branch master <8>\n $ git merge alsa-audio <9>\n $ git log --since='3 days ago' <10>\n $ git log v2.43.. curses/ <11>\n@@ -148,11 +148,11 @@ Clone the upstream and work on it.  Feed changes to upstream.::\n ------------\n $ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6\n $ cd my2.6\n-$ git checkout -b mine master <1>\n+$ git switch-branch -b mine master <1>\n $ edit/compile/test; git commit -a -s <2>\n $ git format-patch master <3>\n $ git send-email --to=\"person <email@example.com>\" 00*.patch <4>\n-$ git checkout master <5>\n+$ git switch-branch master <5>\n $ git pull <6>\n $ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>\n $ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>\n@@ -194,7 +194,7 @@ satellite$ edit/compile/test/commit\n satellite$ git push origin <4>\n \n mothership$ cd frotz\n-mothership$ git checkout master\n+mothership$ git switch-branch master\n mothership$ git merge satellite/master <5>\n ------------\n +\n@@ -216,7 +216,7 @@ machine into the master branch.\n Branch off of a specific tag.::\n +\n ------------\n-$ git checkout -b private2.6.14 v2.6.14 <1>\n+$ git switch-branch -b private2.6.14 v2.6.14 <1>\n $ edit/compile/test; git commit -a\n $ git checkout master\n $ git cherry-pick v2.6.14..private2.6.14 <2>\n@@ -274,14 +274,14 @@ $ mailx <3>\n & s 2 3 4 5 ./+to-apply\n & s 7 8 ./+hold-linus\n & q\n-$ git checkout -b topic/one master\n+$ git switch-branch -b topic/one master\n $ git am -3 -i -s ./+to-apply <4>\n $ compile/test\n-$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>\n-$ git checkout topic/one && git rebase master <6>\n-$ git checkout pu && git reset --hard next <7>\n+$ git switch-branch -b hold/linus && git am -3 -i -s ./+hold-linus <5>\n+$ git switch-branch topic/one && git rebase master <6>\n+$ git switch-branch pu && git reset --hard next <7>\n $ git merge topic/one topic/two && git merge hold/linus <8>\n-$ git checkout maint\n+$ git switch-branch maint\n $ git cherry-pick master~4 <9>\n $ compile/test\n $ git tag -s -m \"GIT 0.99.9x\" v0.99.9x <10>\ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 959044347e..3939ec774a 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -166,7 +166,7 @@ worktree.  The hook is given three parameters: the ref of the previous HEAD,\n the ref of the new HEAD (which may or may not have changed), and a flag\n indicating whether the checkout was a branch checkout (changing branches,\n flag=1) or a file checkout (retrieving a file from the index, flag=0).\n-This hook cannot affect the outcome of `git checkout`.\n+This hook cannot affect the outcome of `git switch-branch` or `git checkout`.\n \n It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is\n used. The first parameter given to the hook is the null-ref, the second the\n@@ -402,7 +402,8 @@ exit with a zero status.\n For example, the hook can simply run `git read-tree -u -m HEAD \"$1\"`\n in order to emulate `git fetch` that is run in the reverse direction\n with `git push`, as the two-tree form of `git read-tree -u -m` is\n-essentially the same as `git checkout` that switches branches while\n+essentially the same as `git switch-branch` or `git checkout`\n+that switches branches while\n keeping the local changes in the working tree that do not interfere\n with the difference between the branches.\n \ndiff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt\nindex e0976f6017..1ec14da6b4 100644\n--- a/Documentation/gittutorial-2.txt\n+++ b/Documentation/gittutorial-2.txt\n@@ -376,7 +376,7 @@ Changes to be committed:\n \n Changes not staged for commit:\n   (use \"git add <file>...\" to update what will be committed)\n-  (use \"git checkout -- <file>...\" to discard changes in working directory)\n+  (use \"git restore-files -- <file>...\" to discard changes in working directory)\n \n \tmodified:   file.txt\n \ndiff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt\nindex 242de31cb6..396e55c191 100644\n--- a/Documentation/gittutorial.txt\n+++ b/Documentation/gittutorial.txt\n@@ -207,7 +207,7 @@ automatically.  The asterisk marks the branch you are currently on;\n type\n \n ------------------------------------------------\n-$ git checkout experimental\n+$ git switch-branch experimental\n ------------------------------------------------\n \n to switch to the experimental branch.  Now edit a file, commit the\n@@ -216,7 +216,7 @@ change, and switch back to the master branch:\n ------------------------------------------------\n (edit file)\n $ git commit -a\n-$ git checkout master\n+$ git switch-branch master\n ------------------------------------------------\n \n Check that the change you made is no longer visible, since it was\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 72daa20e76..f55502cd50 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -115,7 +115,7 @@ Here's an example to make it more clear:\n ------------------------------\n $ git config push.default current\n $ git config remote.pushdefault myfork\n-$ git checkout -b mybranch origin/master\n+$ git switch-branch -b mybranch origin/master\n \n $ git rev-parse --symbolic-full-name @{upstream}\n refs/remotes/origin/master\ndiff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt\nindex eff7890274..e3ff98077d 100644\n--- a/Documentation/user-manual.txt\n+++ b/Documentation/user-manual.txt\n@@ -125,7 +125,7 @@ Create a new branch head pointing to one of these versions and check it\n out using linkgit:git-checkout[1]:\n \n ------------------------------------------------\n-$ git checkout -b new v2.6.13\n+$ git switch-branch -b new v2.6.13\n ------------------------------------------------\n \n The working directory then reflects the contents that the project had\n@@ -282,10 +282,10 @@ a summary of the commands:\n \tthis command will fail with a warning.\n `git branch -D <branch>`::\n \tdelete the branch `<branch>` irrespective of its merged status.\n-`git checkout <branch>`::\n+`git switch-branch <branch>`::\n \tmake the current branch `<branch>`, updating the working\n \tdirectory to reflect the version referenced by `<branch>`.\n-`git checkout -b <new> <start-point>`::\n+`git switch-branch -b <new> <start-point>`::\n \tcreate a new branch `<new>` referencing `<start-point>`, and\n \tcheck it out.\n \n@@ -302,12 +302,12 @@ ref: refs/heads/master\n Examining an old version without creating a new branch\n ------------------------------------------------------\n \n-The `git checkout` command normally expects a branch head, but will also\n+The `git switch-branch` command normally expects a branch head, but will also\n accept an arbitrary commit; for example, you can check out the commit\n referenced by a tag:\n \n ------------------------------------------------\n-$ git checkout v2.6.17\n+$ git switch-branch v2.6.17\n Note: checking out 'v2.6.17'.\n \n You are in 'detached HEAD' state. You can look around, make experimental\n@@ -317,7 +317,7 @@ state without impacting any branches by performing another checkout.\n If you want to create a new branch to retain commits you create, you may\n do so (now or later) by using -b with the checkout command again. Example:\n \n-  git checkout -b new_branch_name\n+  git switch-branch -b new_branch_name\n \n HEAD is now at 427abfa Linux v2.6.17\n ------------------------------------------------\n@@ -373,7 +373,7 @@ You might want to build on one of these remote-tracking branches\n on a branch of your own, just as you would for a tag:\n \n ------------------------------------------------\n-$ git checkout -b my-todo-copy origin/todo\n+$ git switch-branch -b my-todo-copy origin/todo\n ------------------------------------------------\n \n You can also check out `origin/todo` directly to examine it or\n@@ -1523,12 +1523,12 @@ Checking out an old version of a file\n \n In the process of undoing a previous bad change, you may find it\n useful to check out an older version of a particular file using\n-linkgit:git-checkout[1].  We've used `git checkout` before to switch\n+linkgit:git-checkout[1].  We've used `git switch-branch` before to switch\n branches, but it has quite different behavior if it is given a path\n name: the command\n \n -------------------------------------------------\n-$ git checkout HEAD^ path/to/file\n+$ git restore-files HEAD^ path/to/file\n -------------------------------------------------\n \n replaces path/to/file by the contents it had in the commit HEAD^, and\n@@ -2211,8 +2211,8 @@ $ git branch --track release origin/master\n These can be easily kept up to date using linkgit:git-pull[1].\n \n -------------------------------------------------\n-$ git checkout test && git pull\n-$ git checkout release && git pull\n+$ git switch-branch test && git pull\n+$ git switch-branch release && git pull\n -------------------------------------------------\n \n Important note!  If you have any local changes in these branches, then\n@@ -2264,7 +2264,7 @@ tested changes\n 2) help future bug hunters that use `git bisect` to find problems\n \n -------------------------------------------------\n-$ git checkout -b speed-up-spinlocks v2.6.35\n+$ git switch-branch -b speed-up-spinlocks v2.6.35\n -------------------------------------------------\n \n Now you apply the patch(es), run some tests, and commit the change(s).  If\n@@ -2279,7 +2279,7 @@ When you are happy with the state of this change, you can merge it into the\n \"test\" branch in preparation to make it public:\n \n -------------------------------------------------\n-$ git checkout test && git merge speed-up-spinlocks\n+$ git switch-branch test && git merge speed-up-spinlocks\n -------------------------------------------------\n \n It is unlikely that you would have any conflicts here ... but you might if you\n@@ -2291,7 +2291,7 @@ see the value of keeping each patch (or patch series) in its own branch.  It\n means that the patches can be moved into the `release` tree in any order.\n \n -------------------------------------------------\n-$ git checkout release && git merge speed-up-spinlocks\n+$ git switch-branch release && git merge speed-up-spinlocks\n -------------------------------------------------\n \n After a while, you will have a number of branches, and despite the\n@@ -2358,7 +2358,7 @@ Here are some of the scripts that simplify all this even further.\n \n case \"$1\" in\n test|release)\n-\tgit checkout $1 && git pull . origin\n+\tgit switch-branch $1 && git pull . origin\n \t;;\n origin)\n \tbefore=$(git rev-parse refs/remotes/origin/master)\n@@ -2400,7 +2400,7 @@ test|release)\n \t\techo $1 already merged into $2 1>&2\n \t\texit 1\n \tfi\n-\tgit checkout $2 && git pull . $1\n+\tgit switch-branch $2 && git pull . $1\n \t;;\n *)\n \tusage\n@@ -2512,7 +2512,7 @@ Suppose that you create a branch `mywork` on a remote-tracking branch\n `origin`, and create some commits on top of it:\n \n -------------------------------------------------\n-$ git checkout -b mywork origin\n+$ git switch-branch -b mywork origin\n $ vi file.txt\n $ git commit\n $ vi otherfile.txt\n@@ -2552,7 +2552,7 @@ commits without any merges, you may instead choose to use\n linkgit:git-rebase[1]:\n \n -------------------------------------------------\n-$ git checkout mywork\n+$ git switch-branch mywork\n $ git rebase origin\n -------------------------------------------------\n \n@@ -3668,13 +3668,13 @@ change within the submodule, and then update the superproject to reference the\n new commit:\n \n -------------------------------------------------\n-$ git checkout master\n+$ git switch-branch master\n -------------------------------------------------\n \n or\n \n -------------------------------------------------\n-$ git checkout -b fix-up\n+$ git switch-branch -b fix-up\n -------------------------------------------------\n \n then\n@@ -4194,7 +4194,7 @@ start.\n A good place to start is with the contents of the initial commit, with:\n \n ----------------------------------------------------\n-$ git checkout e83c5163\n+$ git switch-branch e83c5163\n ----------------------------------------------------\n \n The initial revision lays the foundation for almost everything Git has\n@@ -4437,10 +4437,10 @@ Managing branches\n -----------------\n \n -----------------------------------------------\n-$ git branch\t     # list all local branches in this repo\n-$ git checkout test  # switch working directory to branch \"test\"\n-$ git branch new     # create branch \"new\" starting at current HEAD\n-$ git branch -d new  # delete branch \"new\"\n+$ git branch\t\t\t# list all local branches in this repo\n+$ git switch-branch test\t# switch working directory to branch \"test\"\n+$ git branch new\t\t# create branch \"new\" starting at current HEAD\n+$ git branch -d new\t\t# delete branch \"new\"\n -----------------------------------------------\n \n Instead of basing a new branch on current HEAD (the default), use:\n@@ -4456,7 +4456,7 @@ $ git branch new test~10 # ten commits before tip of branch \"test\"\n Create and switch to a new branch at the same time:\n \n -----------------------------------------------\n-$ git checkout -b new v2.6.15\n+$ git switch-branch -b new v2.6.15\n -----------------------------------------------\n \n Update and examine branches from the repository you cloned from:\n@@ -4467,7 +4467,7 @@ $ git branch -r\t\t# list\n   origin/master\n   origin/next\n   ...\n-$ git checkout -b masterwork origin/master\n+$ git switch-branch -b masterwork origin/master\n -----------------------------------------------\n \n Fetch a branch from a different repository, and give it a new\ndiff --git a/advice.c b/advice.c\nindex 5f35656409..578ea31c7e 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -189,13 +189,16 @@ void NORETURN die_conclude_merge(void)\n void detach_advice(const char *new_name)\n {\n \tconst char *fmt =\n-\t_(\"Note: checking out '%s'.\\n\\n\"\n+\t_(\"Note: checking out '%s'.\\n\"\n+\t\"\\n\"\n \t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n \t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n+\t\"state without impacting any branches by performing another checkout.\\n\"\n+\t\"\\n\"\n \t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n-\t\"  git checkout -b <new-branch-name>\\n\\n\");\n+\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\"\n+\t\"\\n\"\n+\t\"  git switch-branch -b <new-branch-name>\\n\\n\");\n \n \tfprintf(stderr, fmt, new_name);\n }\ndiff --git a/command-list.txt b/command-list.txt\nindex 4638802754..d1fb1d551d 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -59,7 +59,7 @@ git-cat-file                            plumbinginterrogators\n git-check-attr                          purehelpers\n git-check-ignore                        purehelpers\n git-check-mailmap                       purehelpers\n-git-checkout                            mainporcelain           history\n+git-checkout                            mainporcelain\n git-checkout-index                      plumbingmanipulators\n git-check-ref-format                    purehelpers\n git-cherry                              plumbinginterrogators          complete\ndiff --git a/sha1-name.c b/sha1-name.c\nindex faa60f69e3..4e4e14a45c 100644\n--- a/sha1-name.c\n+++ b/sha1-name.c\n@@ -771,7 +771,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,\n \t\"because it will be ignored when you just specify 40-hex. These refs\\n\"\n \t\"may be created by mistake. For example,\\n\"\n \t\"\\n\"\n-\t\"  git checkout -b $br $(git rev-parse ...)\\n\"\n+\t\"  git switch-branch -b $br $(git rev-parse ...)\\n\"\n \t\"\\n\"\n \t\"where \\\"$br\\\" is somehow empty and a 40-hex ref is created. Please\\n\"\n \t\"examine these refs and maybe delete them. Turn this message off by\\n\"\ndiff --git a/wt-status.c b/wt-status.c\nindex a24711374c..c615cac607 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -224,7 +224,7 @@ static void wt_longstatus_print_dirty_header(struct wt_status *s,\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git add <file>...\\\" to update what will be committed)\"));\n \telse\n \t\tstatus_printf_ln(s, c, _(\"  (use \\\"git add/rm <file>...\\\" to update what will be committed)\"));\n-\tstatus_printf_ln(s, c, _(\"  (use \\\"git checkout -- <file>...\\\" to discard changes in working directory)\"));\n+\tstatus_printf_ln(s, c, _(\"  (use \\\"git restore-files <file>...\\\" to discard changes in working directory)\"));\n \tif (has_dirty_submodules)\n \t\tstatus_printf_ln(s, c, _(\"  (commit or discard the untracked or modified content in submodules)\"));\n \tstatus_printf_ln(s, c, \"%s\", \"\");\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364368","messageId":"20181129215850.7278-8-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 07/14] checkout: split into switch-branch and restore-files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:42Z","receivedAt":"2018-11-29T21:59:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout\" doing too many things is a source of confusion for many\nusers (and it even bites old timers sometimes). To rememdy that, the\ncommand is now split in two: switch-branch and checkout-files. The\ngood old \"git checkout\" command is still here and will be until all\n(or most of users) are sick of it.\n\nSee the new man pages for the final design of these commands. The\nactual implementation though is still pretty much the same as \"git\ncheckout\". Following patches will adjust their behavior to match the\nman pages.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                          |   2 +\n Documentation/git-checkout.txt      |   5 +\n Documentation/git-restore-files.txt | 167 ++++++++++++++++\n Documentation/git-switch-branch.txt | 289 ++++++++++++++++++++++++++++\n Makefile                            |   2 +\n builtin.h                           |   2 +\n builtin/checkout.c                  |  84 ++++++--\n command-list.txt                    |   2 +\n git.c                               |   2 +\n 9 files changed, 543 insertions(+), 12 deletions(-)\n create mode 100644 Documentation/git-restore-files.txt\n create mode 100644 Documentation/git-switch-branch.txt\n\ndiff --git a/.gitignore b/.gitignore\nindex 0d77ea5894..c63dcb1427 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -143,6 +143,7 @@\n /git-request-pull\n /git-rerere\n /git-reset\n+/git-restore-files\n /git-rev-list\n /git-rev-parse\n /git-revert\n@@ -167,6 +168,7 @@\n /git-submodule\n /git-submodule--helper\n /git-svn\n+/git-switch-branch\n /git-symbolic-ref\n /git-tag\n /git-unpack-file\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 25887a6087..25ec7f508f 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -406,6 +406,11 @@ $ edit frotz\n $ git add frotz\n ------------\n \n+SEE ALSO\n+--------\n+linkgit:git-switch-branch[1]\n+linkgit:git-restore-files[1]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-restore-files.txt b/Documentation/git-restore-files.txt\nnew file mode 100644\nindex 0000000000..03c1250ad0\n--- /dev/null\n+++ b/Documentation/git-restore-files.txt\n@@ -0,0 +1,167 @@\n+git-restore-files(1)\n+====================\n+\n+NAME\n+----\n+git-restore-files - Restore working tree files\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git restore-files' [-f|--ours|--theirs|-m|--conflict=<style>] [--from=<tree-ish>] <pathspec>...\n+'git restore-files' [--from=<tree-ish>] <pathspec>...\n+'git restore-files' (-p|--patch) [--from=<tree-ish>] [<pathspec>...]\n+\n+DESCRIPTION\n+-----------\n+Updates files in the working tree to match the version in the index\n+or the specified tree.\n+\n+'git restore-files' [--from=<tree-ish>] <pathspec>...::\n+\n+\tOverwrite paths in the working tree by replacing with the\n+\tcontents in the index or in the <tree-ish> (most often a\n+\tcommit).  When a <tree-ish> is given, the paths that\n+\tmatch the <pathspec> are updated both in the index and in\n+\tthe working tree.\n++\n+The index may contain unmerged entries because of a previous failed merge.\n+By default, if you try to check out such an entry from the index, the\n+checkout operation will fail and nothing will be checked out.\n+Using `-f` will ignore these unmerged entries.  The contents from a\n+specific side of the merge can be checked out of the index by\n+using `--ours` or `--theirs`.  With `-m`, changes made to the working tree\n+file can be discarded to re-create the original conflicted merge result.\n+\n+'git restore-files' (-p|--patch) [--from=<tree-ish>] [<pathspec>...]::\n+\tThis is similar to the \"check out paths to the working tree\n+\tfrom either the index or from a tree-ish\" mode described\n+\tabove, but lets you use the interactive interface to show\n+\tthe \"diff\" output and choose which hunks to use in the\n+\tresult.  See below for the description of `--patch` option.\n+\n+OPTIONS\n+-------\n+-q::\n+--quiet::\n+\tQuiet, suppress feedback messages.\n+\n+--[no-]progress::\n+\tProgress status is reported on the standard error stream\n+\tby default when it is attached to a terminal, unless `--quiet`\n+\tis specified. This flag enables progress reporting even if not\n+\tattached to a terminal, regardless of `--quiet`.\n+\n+-f::\n+--force::\n+\tDo not fail upon unmerged entries; instead, unmerged entries\n+\tare ignored.\n+\n+--ours::\n+--theirs::\n+\tCheck out stage #2 ('ours') or #3 ('theirs') for unmerged\n+\tpaths.\n++\n+Note that during `git rebase` and `git pull --rebase`, 'ours' and\n+'theirs' may appear swapped; `--ours` gives the version from the\n+branch the changes are rebased onto, while `--theirs` gives the\n+version from the branch that holds your work that is being rebased.\n++\n+This is because `rebase` is used in a workflow that treats the\n+history at the remote as the shared canonical one, and treats the\n+work done on the branch you are rebasing as the third-party work to\n+be integrated, and you are temporarily assuming the role of the\n+keeper of the canonical history during the rebase.  As the keeper of\n+the canonical history, you need to view the history from the remote\n+as `ours` (i.e. \"our shared canonical history\"), while what you did\n+on your side branch as `theirs` (i.e. \"one contributor's work on top\n+of it\").\n+\n+--ignore-skip-worktree-bits::\n+\tIn sparse checkout mode, update only entries matched by\n+\t<paths> and sparse patterns in\n+\t$GIT_DIR/info/sparse-checkout. This option ignores the sparse\n+\tpatterns and adds back any files in <paths>.\n+\n+-m::\n+--merge::\n+\tWhen checking out paths from the index, this option lets you\n+\trecreate the conflicted merge in the specified paths.\n+\n+--conflict=<style>::\n+\tThe same as --merge option above, but changes the way the\n+\tconflicting hunks are presented, overriding the\n+\tmerge.conflictStyle configuration variable.  Possible values are\n+\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n+\t\"merge\" style, shows the original contents).\n+\n+-p::\n+--patch::\n+\tInteractively select hunks in the difference between the\n+\t<tree-ish> (or the index, if unspecified) and the working\n+\ttree.  The chosen hunks are then applied in reverse to the\n+\tworking tree (and if a <tree-ish> was specified, the index).\n++\n+This means that you can use `git restore-files -p` to selectively\n+discard edits from your current working tree. See the ``Interactive\n+Mode'' section of linkgit:git-add[1] to learn how to operate the\n+`--patch` mode.\n+\n+--[no-]recurse-submodules::\n+\tUsing --recurse-submodules will update the content of all initialized\n+\tsubmodules according to the commit recorded in the superproject. If\n+\tlocal modifications in a submodule would be overwritten the checkout\n+\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n+\tis used, the work trees of submodules will not be updated.\n+\tJust like linkgit:git-submodule[1], this will detach the\n+\tsubmodules HEAD.\n+\n+<tree-ish>::\n+\tTree to checkout from (when paths are given). If not specified,\n+\tthe index will be used.\n+\n+EXAMPLES\n+--------\n+\n+. The following sequence checks out the `master` branch, reverts\n+the `Makefile` to two revisions back, deletes hello.c by\n+mistake, and gets it back from the index.\n++\n+------------\n+$ git switch-branch master                    <1>\n+$ git restore-files --from master~2 Makefile  <2>\n+$ rm -f hello.c\n+$ git restore-files hello.c                   <3>\n+------------\n++\n+<1> switch branch\n+<2> take a file out of another commit\n+<3> restore hello.c from the index\n++\n+If you want to check out _all_ C source files out of the index,\n+you can say\n++\n+------------\n+$ git restore-files '*.c'\n+------------\n++\n+Note the quotes around `*.c`.  The file `hello.c` will also be\n+checked out, even though it is no longer in the working tree,\n+because the file globbing is used to match entries in the index\n+(not in the working tree by the shell).\n++\n+If you have an unfortunate branch that is named `hello.c`, this\n+step would be confused as an instruction to switch to that branch.\n+You should instead write:\n++\n+------------\n+$ git restore-files hello.c\n+------------\n+\n+SEE ALSO\n+--------\n+linkgit:git-checkout[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-switch-branch.txt b/Documentation/git-switch-branch.txt\nnew file mode 100644\nindex 0000000000..d5bf5cb37d\n--- /dev/null\n+++ b/Documentation/git-switch-branch.txt\n@@ -0,0 +1,289 @@\n+git-switch-branch(1)\n+====================\n+\n+NAME\n+----\n+git-switch-branch - Switch branches\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git switch-branch' [-q] [-f] [-m] <branch>\n+'git switch-branch' [-q] [-f] [-m] --detach [<commit>]\n+'git switch-branch' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]\n+\n+DESCRIPTION\n+-----------\n+Switch to a specified branch and update files in the working tree to\n+match it.\n+\n+'git switch-branch' <branch>::\n+\tTo prepare for working on <branch>, switch to it by updating\n+\tthe index and the files in the working tree. Local\n+\tmodifications to the files in the working tree are kept, so\n+\tthat they can be committed to the <branch>.\n++\n+If <branch> is not found but there does exist a tracking branch in\n+exactly one remote (call it <remote>) with a matching name, treat as\n+equivalent to\n++\n+------------\n+$ git switch-branch -b <branch> --track <remote>/<branch>\n+------------\n++\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n+\n+'git switch-branch' -c|-C <new_branch> [<start_point>]::\n+\n+\tSpecifying `-c` causes a new branch to be created as if\n+\tlinkgit:git-branch[1] were called and then switched to. In\n+\tthis case you can use the `--track` or `--no-track` options,\n+\twhich will be passed to 'git branch'.  As a convenience,\n+\t`--track` without `-c` implies branch creation; see the\n+\tdescription of `--track` below.\n++\n+If `-C` is given, <new_branch> is created if it doesn't exist;\n+otherwise, it is reset. This is the transactional equivalent of\n++\n+------------\n+$ git branch -f <branch> [<start_point>]\n+$ git switch-branch <branch>\n+------------\n++\n+that is to say, the branch is not reset/created unless \"git\n+switch-branch\" is successful.\n+\n+'git switch-branch' --detach [<commit>]::\n+\n+\tPrepare to work on a unnamed branch on top of <commit> (see\n+\t\"DETACHED HEAD\" section), and updating the index and the files\n+\tin the working tree.  Local modifications to the files in the\n+\tworking tree are kept, so that the resulting working tree will\n+\tbe the state recorded in the commit plus the local\n+\tmodifications.\n++\n+When the <commit> argument is a branch name, the `--detach` option can\n+be used to detach HEAD at the tip of the branch (`git switch-branch\n+<branch>` would check out that branch without detaching HEAD).\n++\n+Omitting <commit> detaches HEAD at the tip of the current branch.\n+\n+OPTIONS\n+-------\n+-q::\n+--quiet::\n+\tQuiet, suppress feedback messages.\n+\n+--[no-]progress::\n+\tProgress status is reported on the standard error stream\n+\tby default when it is attached to a terminal, unless `--quiet`\n+\tis specified. This flag enables progress reporting even if not\n+\tattached to a terminal, regardless of `--quiet`.\n+\n+-f::\n+--force::\n+\tProceed even if the index or the working tree differs from\n+\tHEAD.  This is used to throw away local changes.\n+\n+-c <new_branch>::\n+--create <new_branch>::\n+\tCreate a new branch named <new_branch> and start it at\n+\t<start_point>; see linkgit:git-branch[1] for details.\n+\n+-C <new_branch>::\n+--force-create <new_branch>::\n+\tCreates the branch <new_branch> and start it at <start_point>;\n+\tif it already exists, then reset it to <start_point>. This is\n+\tequivalent to running \"git branch\" with \"-f\"; see\n+\tlinkgit:git-branch[1] for details.\n+\n+-t::\n+--track::\n+\tWhen creating a new branch, set up \"upstream\" configuration. See\n+\t\"--track\" in linkgit:git-branch[1] for details.\n++\n+If no `-c` option is given, the name of the new branch will be derived\n+from the remote-tracking branch, by looking at the local part of the\n+refspec configured for the corresponding remote, and then stripping\n+the initial part up to the \"*\".\n+This would tell us to use \"hack\" as the local branch when branching\n+off of \"origin/hack\" (or \"remotes/origin/hack\", or even\n+\"refs/remotes/origin/hack\").  If the given name has no slash, or the above\n+guessing results in an empty name, the guessing is aborted.  You can\n+explicitly give a name with `-c` in such a case.\n+\n+--no-track::\n+\tDo not set up \"upstream\" configuration, even if the\n+\tbranch.autoSetupMerge configuration variable is true.\n+\n+-l::\n+\tCreate the new branch's reflog; see linkgit:git-branch[1] for\n+\tdetails.\n+\n+--detach::\n+\tRather than checking out a branch to work on it, check out a\n+\tcommit for inspection and discardable experiments.\n+\tThis is the default behavior of \"git checkout <commit>\" when\n+\t<commit> is not a branch name.  See the \"DETACHED HEAD\" section\n+\tbelow for details.\n+\n+--orphan <new_branch>::\n+\tCreate a new 'orphan' branch, named <new_branch>, started from\n+\t<start_point> and switch to it.  The first commit made on this\n+\tnew branch will have no parents and it will be the root of a new\n+\thistory totally disconnected from all the other branches and\n+\tcommits.\n++\n+The index and the working tree are adjusted as if you had previously run\n+\"git checkout <start_point>\".  This allows you to start a new history\n+that records a set of paths similar to <start_point> by easily running\n+\"git commit -a\" to make the root commit.\n++\n+This can be useful when you want to publish the tree from a commit\n+without exposing its full history. You might want to do this to publish\n+an open source branch of a project whose current tree is \"clean\", but\n+whose full history contains proprietary or otherwise encumbered bits of\n+code.\n++\n+If you want to start a disconnected history that records a set of paths\n+that is totally different from the one of <start_point>, then you should\n+clear the index and the working tree right after creating the orphan\n+branch by running \"git rm -rf .\" from the top level of the working tree.\n+Afterwards you will be ready to prepare your new files, repopulating the\n+working tree, by copying them from elsewhere, extracting a tarball, etc.\n+\n+-m::\n+--merge::\n+\tIf you have local modifications to one or more files that are\n+\tdifferent between the current branch and the branch to which\n+\tyou are switching, the command refuses to switch branches in\n+\torder to preserve your modifications in context.  However,\n+\twith this option, a three-way merge between the current\n+\tbranch, your working tree contents, and the new branch is\n+\tdone, and you will be on the new branch.\n++\n+When a merge conflict happens, the index entries for conflicting\n+paths are left unmerged, and you need to resolve the conflicts\n+and mark the resolved paths with `git add` (or `git rm` if the merge\n+should result in deletion of the path).\n+\n+--conflict=<style>::\n+\tThe same as --merge option above, but changes the way the\n+\tconflicting hunks are presented, overriding the\n+\tmerge.conflictStyle configuration variable.  Possible values are\n+\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n+\t\"merge\" style, shows the original contents).\n+\n+--ignore-other-worktrees::\n+\t`git switch-branch` refuses when the wanted ref is already\n+\tchecked out by another worktree. This option makes it check\n+\tthe ref out anyway. In other words, the ref can be held by\n+\tmore than one worktree.\n+\n+--[no-]recurse-submodules::\n+\tUsing --recurse-submodules will update the content of all initialized\n+\tsubmodules according to the commit recorded in the superproject. If\n+\tlocal modifications in a submodule would be overwritten the checkout\n+\twill fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n+\tis used, the work trees of submodules will not be updated.\n+\tJust like linkgit:git-submodule[1], this will detach the\n+\tsubmodules HEAD.\n+\n+<branch>::\n+\tBranch to checkout; if it refers to a branch (i.e., a name that,\n+\twhen prepended with \"refs/heads/\", is a valid ref), then that\n+\tbranch is checked out. Otherwise, if it refers to a valid\n+\tcommit, your HEAD becomes \"detached\" and you are no longer on\n+\tany branch (see below for details).\n++\n+You can use the `\"@{-N}\"` syntax to refer to the N-th last\n+branch/commit checked out using \"git checkout\" operation. You may\n+also specify `-` which is synonymous to `\"@{-1}`.\n++\n+As a special case, you may use `\"A...B\"` as a shortcut for the\n+merge base of `A` and `B` if there is exactly one merge base. You can\n+leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n+\n+<new_branch>::\n+\tName for the new branch.\n+\n+<start_point>::\n+\tThe name of a commit at which to start the new branch; see\n+\tlinkgit:git-branch[1] for details. Defaults to HEAD.\n+\n+DETACHED HEAD\n+-------------\n+include::detach-head.txt[]\n+\n+EXAMPLES\n+--------\n+\n+. The following sequence checks out the `master` branch.\n++\n+------------\n+$ git switch-branch master\n+------------\n++\n+\n+. After working in the wrong branch, switching to the correct\n+branch would be done using:\n++\n+------------\n+$ git switch-branch mytopic\n+------------\n++\n+However, your \"wrong\" branch and correct \"mytopic\" branch may\n+differ in files that you have modified locally, in which case\n+the above checkout would fail like this:\n++\n+------------\n+$ git switch-branch mytopic\n+error: You have local changes to 'frotz'; not switching branches.\n+------------\n++\n+You can give the `-m` flag to the command, which would try a\n+three-way merge:\n++\n+------------\n+$ git switch-branch -m mytopic\n+Auto-merging frotz\n+------------\n++\n+After this three-way merge, the local modifications are _not_\n+registered in your index file, so `git diff` would show you what\n+changes you made since the tip of the new branch.\n+\n+. When a merge conflict happens during switching branches with\n+the `-m` option, you would see something like this:\n++\n+------------\n+$ git switch-branch -m mytopic\n+Auto-merging frotz\n+ERROR: Merge conflict in frotz\n+fatal: merge program failed\n+------------\n++\n+At this point, `git diff` shows the changes cleanly merged as in\n+the previous example, as well as the changes in the conflicted\n+files.  Edit and resolve the conflict and mark it resolved with\n+`git add` as usual:\n++\n+------------\n+$ edit frotz\n+$ git add frotz\n+------------\n+\n+SEE ALSO\n+--------\n+linkgit:git-checkout[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/Makefile b/Makefile\nindex 1a44c811aa..f035dbab9e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -777,9 +777,11 @@ BUILT_INS += git-format-patch$X\n BUILT_INS += git-fsck-objects$X\n BUILT_INS += git-init$X\n BUILT_INS += git-merge-subtree$X\n+BUILT_INS += git-restore-files$X\n BUILT_INS += git-show$X\n BUILT_INS += git-stage$X\n BUILT_INS += git-status$X\n+BUILT_INS += git-switch-branch$X\n BUILT_INS += git-whatchanged$X\n \n # what 'all' will build and 'install' will install in gitexecdir,\ndiff --git a/builtin.h b/builtin.h\nindex 6538932e99..01ed43ea69 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -214,6 +214,7 @@ extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);\n extern int cmd_repack(int argc, const char **argv, const char *prefix);\n extern int cmd_rerere(int argc, const char **argv, const char *prefix);\n extern int cmd_reset(int argc, const char **argv, const char *prefix);\n+extern int cmd_restore_files(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);\n extern int cmd_revert(int argc, const char **argv, const char *prefix);\n@@ -227,6 +228,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n extern int cmd_status(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n+extern int cmd_switch_branch(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_tag(int argc, const char **argv, const char *prefix);\n extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 764e1a83a1..7dc0f4d3f3 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -33,6 +33,16 @@ static const char * const checkout_usage[] = {\n \tNULL,\n };\n \n+static const char * const switch_branch_usage[] = {\n+\tN_(\"git switch-branch [<options>] [<branch>]\"),\n+\tNULL,\n+};\n+\n+static const char * const restore_files_usage[] = {\n+\tN_(\"git restore-files [<options>] [<branch>] -- <file>...\"),\n+\tNULL,\n+};\n+\n struct checkout_opts {\n \tint patch_mode;\n \tint quiet;\n@@ -1302,31 +1312,23 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n \treturn newopts;\n }\n \n-int cmd_checkout(int argc, const char **argv, const char *prefix)\n+static int checkout_main(int argc, const char **argv, const char *prefix,\n+\t\t\t struct checkout_opts *opts, struct option *options,\n+\t\t\t const char * const usagestr[])\n {\n-\tstruct checkout_opts real_opts;\n-\tstruct checkout_opts *opts = &real_opts;\n \tstruct branch_info new_branch_info;\n \tint dwim_remotes_matched = 0;\n-\tstruct option *options = NULL;\n \n-\tmemset(opts, 0, sizeof(*opts));\n \tmemset(&new_branch_info, 0, sizeof(new_branch_info));\n \topts->overwrite_ignore = 1;\n \topts->prefix = prefix;\n \topts->show_progress = -1;\n-\topts->dwim_new_local_branch = 1;\n \n \tgit_config(git_checkout_config, opts);\n \n \topts->track = BRANCH_TRACK_UNSPECIFIED;\n \n-\toptions = parse_options_dup(options);\n-\toptions = add_common_options(opts, options);\n-\toptions = add_switch_branch_options(opts, options);\n-\toptions = add_checkout_path_options(opts, options);\n-\n-\targc = parse_options(argc, argv, prefix, options, checkout_usage,\n+\targc = parse_options(argc, argv, prefix, options, usagestr,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n \n \tif (opts->show_progress < 0) {\n@@ -1455,3 +1457,61 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\treturn checkout_branch(opts, &new_branch_info);\n \t}\n }\n+\n+int cmd_checkout(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.dwim_new_local_branch = 1;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, checkout_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\n+\n+int cmd_switch_branch(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.dwim_new_local_branch = 1;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_switch_branch_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, switch_branch_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\n+\n+int cmd_restore_files(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct checkout_opts opts;\n+\tstruct option *options = NULL;\n+\tint ret;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.dwim_new_local_branch = 1;\n+\n+\toptions = parse_options_dup(options);\n+\toptions = add_common_options(&opts, options);\n+\toptions = add_checkout_path_options(&opts, options);\n+\n+\tret = checkout_main(argc, argv, prefix, &opts,\n+\t\t\t    options, restore_files_usage);\n+\tFREE_AND_NULL(options);\n+\treturn ret;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 3a9af104b5..4638802754 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -151,6 +151,7 @@ git-replace                             ancillarymanipulators           complete\n git-request-pull                        foreignscminterface             complete\n git-rerere                              ancillaryinterrogators\n git-reset                               mainporcelain           worktree\n+git-restore-files                       mainporcelain           worktree\n git-revert                              mainporcelain\n git-rev-list                            plumbinginterrogators\n git-rev-parse                           plumbinginterrogators\n@@ -171,6 +172,7 @@ git-status                              mainporcelain           info\n git-stripspace                          purehelpers\n git-submodule                           mainporcelain\n git-svn                                 foreignscminterface\n+git-switch-branch                       mainporcelain           history\n git-symbolic-ref                        plumbingmanipulators\n git-tag                                 mainporcelain           history\n git-unpack-file                         plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 2f604a41ea..a2be6c3eb5 100644\n--- a/git.c\n+++ b/git.c\n@@ -542,6 +542,7 @@ static struct cmd_struct commands[] = {\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n+\t{ \"restore-files\", cmd_restore_files, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"rev-list\", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },\n \t{ \"rev-parse\", cmd_rev_parse, NO_PARSEOPT },\n \t{ \"revert\", cmd_revert, RUN_SETUP | NEED_WORK_TREE },\n@@ -557,6 +558,7 @@ static struct cmd_struct commands[] = {\n \t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"stripspace\", cmd_stripspace },\n \t{ \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n+\t{ \"switch-branch\", cmd_switch_branch, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n \t{ \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n \t{ \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364369","messageId":"20181129215850.7278-3-pclouds@gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"[PATCH v3 02/14] git-checkout.txt: split detached head section out","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-29T21:58:37Z","receivedAt":"2018-11-29T21:59:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is to be reused by the coming git-switch-branch.txt man page\nwhich also deals with detached HEAD.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/detach-head.txt  | 132 ++++++++++++++++++++++++++++++++\n Documentation/git-checkout.txt | 133 +--------------------------------\n 2 files changed, 133 insertions(+), 132 deletions(-)\n create mode 100644 Documentation/detach-head.txt\n\ndiff --git a/Documentation/detach-head.txt b/Documentation/detach-head.txt\nnew file mode 100644\nindex 0000000000..bb6f5d7843\n--- /dev/null\n+++ b/Documentation/detach-head.txt\n@@ -0,0 +1,132 @@\n+HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each\n+branch refers to a specific commit. Let's look at a repo with three\n+commits, one of them tagged, and with branch 'master' checked out:\n+\n+------------\n+           HEAD (refers to branch 'master')\n+            |\n+            v\n+a---b---c  branch 'master' (refers to commit 'c')\n+    ^\n+    |\n+  tag 'v2.0' (refers to commit 'b')\n+------------\n+\n+When a commit is created in this state, the branch is updated to refer to\n+the new commit. Specifically, 'git commit' creates a new commit 'd', whose\n+parent is commit 'c', and then updates branch 'master' to refer to new\n+commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers\n+to commit 'd':\n+\n+------------\n+$ edit; git add; git commit\n+\n+               HEAD (refers to branch 'master')\n+                |\n+                v\n+a---b---c---d  branch 'master' (refers to commit 'd')\n+    ^\n+    |\n+  tag 'v2.0' (refers to commit 'b')\n+------------\n+\n+It is sometimes useful to be able to checkout a commit that is not at\n+the tip of any named branch, or even to create a new commit that is not\n+referenced by a named branch. Let's look at what happens when we\n+checkout commit 'b' (here we show two ways this may be done):\n+\n+------------\n+$ git checkout v2.0  # or\n+$ git checkout master^^\n+\n+   HEAD (refers to commit 'b')\n+    |\n+    v\n+a---b---c---d  branch 'master' (refers to commit 'd')\n+    ^\n+    |\n+  tag 'v2.0' (refers to commit 'b')\n+------------\n+\n+Notice that regardless of which checkout command we use, HEAD now refers\n+directly to commit 'b'. This is known as being in detached HEAD state.\n+It means simply that HEAD refers to a specific commit, as opposed to\n+referring to a named branch. Let's see what happens when we create a commit:\n+\n+------------\n+$ edit; git add; git commit\n+\n+     HEAD (refers to commit 'e')\n+      |\n+      v\n+      e\n+     /\n+a---b---c---d  branch 'master' (refers to commit 'd')\n+    ^\n+    |\n+  tag 'v2.0' (refers to commit 'b')\n+------------\n+\n+There is now a new commit 'e', but it is referenced only by HEAD. We can\n+of course add yet another commit in this state:\n+\n+------------\n+$ edit; git add; git commit\n+\n+         HEAD (refers to commit 'f')\n+          |\n+          v\n+      e---f\n+     /\n+a---b---c---d  branch 'master' (refers to commit 'd')\n+    ^\n+    |\n+  tag 'v2.0' (refers to commit 'b')\n+------------\n+\n+In fact, we can perform all the normal Git operations. But, let's look\n+at what happens when we then checkout master:\n+\n+------------\n+$ git checkout master\n+\n+               HEAD (refers to branch 'master')\n+      e---f     |\n+     /          v\n+a---b---c---d  branch 'master' (refers to commit 'd')\n+    ^\n+    |\n+  tag 'v2.0' (refers to commit 'b')\n+------------\n+\n+It is important to realize that at this point nothing refers to commit\n+'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted\n+by the routine Git garbage collection process, unless we create a reference\n+before that happens. If we have not yet moved away from commit 'f',\n+any of these will create a reference to it:\n+\n+------------\n+$ git checkout -b foo   <1>\n+$ git branch foo        <2>\n+$ git tag foo           <3>\n+------------\n+\n+<1> creates a new branch 'foo', which refers to commit 'f', and then\n+updates HEAD to refer to branch 'foo'. In other words, we'll no longer\n+be in detached HEAD state after this command.\n+\n+<2> similarly creates a new branch 'foo', which refers to commit 'f',\n+but leaves HEAD detached.\n+\n+<3> creates a new tag 'foo', which refers to commit 'f',\n+leaving HEAD detached.\n+\n+If we have moved away from commit 'f', then we must first recover its object\n+name (typically by using git reflog), and then we can create a reference to\n+it. For example, to see the last two commits to which HEAD referred, we\n+can use either of these commands:\n+\n+------------\n+$ git reflog -2 HEAD # or\n+$ git log -g -2 HEAD\n+------------\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 65bd1bc50d..25887a6087 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -306,138 +306,7 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n \n DETACHED HEAD\n -------------\n-HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each\n-branch refers to a specific commit. Let's look at a repo with three\n-commits, one of them tagged, and with branch 'master' checked out:\n-\n-------------\n-           HEAD (refers to branch 'master')\n-            |\n-            v\n-a---b---c  branch 'master' (refers to commit 'c')\n-    ^\n-    |\n-  tag 'v2.0' (refers to commit 'b')\n-------------\n-\n-When a commit is created in this state, the branch is updated to refer to\n-the new commit. Specifically, 'git commit' creates a new commit 'd', whose\n-parent is commit 'c', and then updates branch 'master' to refer to new\n-commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers\n-to commit 'd':\n-\n-------------\n-$ edit; git add; git commit\n-\n-               HEAD (refers to branch 'master')\n-                |\n-                v\n-a---b---c---d  branch 'master' (refers to commit 'd')\n-    ^\n-    |\n-  tag 'v2.0' (refers to commit 'b')\n-------------\n-\n-It is sometimes useful to be able to checkout a commit that is not at\n-the tip of any named branch, or even to create a new commit that is not\n-referenced by a named branch. Let's look at what happens when we\n-checkout commit 'b' (here we show two ways this may be done):\n-\n-------------\n-$ git checkout v2.0  # or\n-$ git checkout master^^\n-\n-   HEAD (refers to commit 'b')\n-    |\n-    v\n-a---b---c---d  branch 'master' (refers to commit 'd')\n-    ^\n-    |\n-  tag 'v2.0' (refers to commit 'b')\n-------------\n-\n-Notice that regardless of which checkout command we use, HEAD now refers\n-directly to commit 'b'. This is known as being in detached HEAD state.\n-It means simply that HEAD refers to a specific commit, as opposed to\n-referring to a named branch. Let's see what happens when we create a commit:\n-\n-------------\n-$ edit; git add; git commit\n-\n-     HEAD (refers to commit 'e')\n-      |\n-      v\n-      e\n-     /\n-a---b---c---d  branch 'master' (refers to commit 'd')\n-    ^\n-    |\n-  tag 'v2.0' (refers to commit 'b')\n-------------\n-\n-There is now a new commit 'e', but it is referenced only by HEAD. We can\n-of course add yet another commit in this state:\n-\n-------------\n-$ edit; git add; git commit\n-\n-\t HEAD (refers to commit 'f')\n-\t  |\n-\t  v\n-      e---f\n-     /\n-a---b---c---d  branch 'master' (refers to commit 'd')\n-    ^\n-    |\n-  tag 'v2.0' (refers to commit 'b')\n-------------\n-\n-In fact, we can perform all the normal Git operations. But, let's look\n-at what happens when we then checkout master:\n-\n-------------\n-$ git checkout master\n-\n-               HEAD (refers to branch 'master')\n-      e---f     |\n-     /          v\n-a---b---c---d  branch 'master' (refers to commit 'd')\n-    ^\n-    |\n-  tag 'v2.0' (refers to commit 'b')\n-------------\n-\n-It is important to realize that at this point nothing refers to commit\n-'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted\n-by the routine Git garbage collection process, unless we create a reference\n-before that happens. If we have not yet moved away from commit 'f',\n-any of these will create a reference to it:\n-\n-------------\n-$ git checkout -b foo   <1>\n-$ git branch foo        <2>\n-$ git tag foo           <3>\n-------------\n-\n-<1> creates a new branch 'foo', which refers to commit 'f', and then\n-updates HEAD to refer to branch 'foo'. In other words, we'll no longer\n-be in detached HEAD state after this command.\n-\n-<2> similarly creates a new branch 'foo', which refers to commit 'f',\n-but leaves HEAD detached.\n-\n-<3> creates a new tag 'foo', which refers to commit 'f',\n-leaving HEAD detached.\n-\n-If we have moved away from commit 'f', then we must first recover its object\n-name (typically by using git reflog), and then we can create a reference to\n-it. For example, to see the last two commits to which HEAD referred, we\n-can use either of these commands:\n-\n-------------\n-$ git reflog -2 HEAD # or\n-$ git log -g -2 HEAD\n-------------\n+include::detach-head.txt[]\n \n ARGUMENT DISAMBIGUATION\n -----------------------\n-- \n2.20.0.rc1.380.g3eb999425c.dirty\n\n"},{"id":"364370","messageId":"87pnunxz5i.fsf@evledraar.gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-11-29T23:05:45Z","receivedAt":"2018-11-29T23:05:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Nov 29 2018, Nguyễn Thái Ngọc Duy wrote:\n\n> v3 sees switch-branch go back to switch-branch (in v2 it was\n> checkout-branch). checkout-files is also renamed restore-files (v1 was\n> restore-paths). Hopefully we won't see another rename.\n>\n> I'll try to summarize the differences between the new commands and\n> 'git checkout' down here, but you're welcome to just head to 07/14 and\n> read the new man pages.\n>\n> 'git switch-branch'\n>\n> - does not \"do nothing\", you have to either switch branch, create a\n>   new branch, or detach. \"git switch-branch\" with no arguments is\n>   rejected.\n>\n> - implicit detaching is rejected. If you need to detach, you need to\n>   give --detach. Or stick to 'git checkout'.\n>\n> - -b/-B is renamed to -c/-C with long option names\n>\n> - of course does not accept pathspec\n>\n> 'git restore-files'\n>\n> - takes a ref from --from argument, not as a free ref. As a result,\n>   '--' is no longer needed. All non-option arguments are pathspec\n>\n> - pathspec is mandatory, you can't do \"git restore-files\" without any\n>   pathspec.\n>\n> - I just remember -p which is allowed to take no pathspec :( I'll fix\n>   it later.\n>\n> - Two more fancy features (the \"git checkout --index\" being the\n>   default mode and the backup log for accidental overwrites) are of\n>   course still missing. But they are coming.\n>\n> I did not go replace \"detached HEAD\" with \"unnamed branch\" (or \"no\n> branch\") everywhere because I think a unique term is still good to\n> refer to this concept. Or maybe \"no branch\" is good enough. I dunno.\n\nI finally tracked down\nhttps://redfin.engineering/two-commits-that-wrecked-the-user-experience-of-git-f0075b77eab1\nwhich I'd remembered reading and couldn't find again in these\ndiscussions. Re-reading it while one may not 100% agree with the\nauthor's opinion, it's an interesting rabbit hole.\n\nI also didn't know about EasyGit, or that Elijah Newren had written\nit. I haven't seen him chime in on this series, and would be interested\nto see what he thinks about it.\n\nRe the naming question in\nhttps://public-inbox.org/git/87o9abzv46.fsf@evledraar.gmail.com/ &\nseeing that eg-switch exists, I wonder if just s/switch-branch/switch/\nmakes more sense.\n\nAssuming greenfield development (which we definitely don't have), I\ndon't like the \"restore-files\" name, but the alternative that makes\nsense is \"checkout\". Then this \"--from\" argument could become \"git\ncheckout-tree <treeish> -- <pathspec>\", and we'd have:\n\n    git switch <branchish>\n    git checkout <pathspec>\n    git checkout-tree <treeish> -- <pathspec>\n\nOr maybe that sucks, anyway what I was going to suggest is *if* others\nthink that made sense as a \"if we designed git today\" endgame whether we\ncould have an opt-in setting where you set e.g. core.uiVersion=3 (in\nanticipation of Git 3.0) and you'd get that behavior. There could be\nsome other setting where core.uiVersion would use the old behavior (or\nwith another setting, only warn) if we weren't connected to a terminal.\n\nI.e. I'm thinking of this as step #2 in a #3 step series. Where this is\nthe fully backwards compatible UI improvement, but someone who'd\ne.g. use EasyGit or didn't have backwards compatibility concerns could\nenable step #3 and opt-in to a mode where we'd fixed a bunch of UI warts\nin a backwards-incompatible way.\n\nWhat would that mode look like? I'd to work on piling that on top of\nthis :)\n"},{"id":"364371","messageId":"87o9a7xyji.fsf@evledraar.gmail.com","threadId":"49793","inReplyTo":"87pnunxz5i.fsf@evledraar.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-11-29T23:18:57Z","receivedAt":"2018-11-29T23:19:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Nov 29 2018, Ævar Arnfjörð Bjarmason wrote:\n\n> On Thu, Nov 29 2018, Nguyễn Thái Ngọc Duy wrote:\n>\n>> v3 sees switch-branch go back to switch-branch (in v2 it was\n>> checkout-branch). checkout-files is also renamed restore-files (v1 was\n>> restore-paths). Hopefully we won't see another rename.\n>>\n>> I'll try to summarize the differences between the new commands and\n>> 'git checkout' down here, but you're welcome to just head to 07/14 and\n>> read the new man pages.\n>>\n>> 'git switch-branch'\n>>\n>> - does not \"do nothing\", you have to either switch branch, create a\n>>   new branch, or detach. \"git switch-branch\" with no arguments is\n>>   rejected.\n>>\n>> - implicit detaching is rejected. If you need to detach, you need to\n>>   give --detach. Or stick to 'git checkout'.\n>>\n>> - -b/-B is renamed to -c/-C with long option names\n>>\n>> - of course does not accept pathspec\n>>\n>> 'git restore-files'\n>>\n>> - takes a ref from --from argument, not as a free ref. As a result,\n>>   '--' is no longer needed. All non-option arguments are pathspec\n>>\n>> - pathspec is mandatory, you can't do \"git restore-files\" without any\n>>   pathspec.\n>>\n>> - I just remember -p which is allowed to take no pathspec :( I'll fix\n>>   it later.\n>>\n>> - Two more fancy features (the \"git checkout --index\" being the\n>>   default mode and the backup log for accidental overwrites) are of\n>>   course still missing. But they are coming.\n>>\n>> I did not go replace \"detached HEAD\" with \"unnamed branch\" (or \"no\n>> branch\") everywhere because I think a unique term is still good to\n>> refer to this concept. Or maybe \"no branch\" is good enough. I dunno.\n>\n> I finally tracked down\n> https://redfin.engineering/two-commits-that-wrecked-the-user-experience-of-git-f0075b77eab1\n> which I'd remembered reading and couldn't find again in these\n> discussions. Re-reading it while one may not 100% agree with the\n> author's opinion, it's an interesting rabbit hole.\n>\n> I also didn't know about EasyGit, or that Elijah Newren had written\n> it. I haven't seen him chime in on this series, and would be interested\n> to see what he thinks about it.\n>\n> Re the naming question in\n> https://public-inbox.org/git/87o9abzv46.fsf@evledraar.gmail.com/ &\n> seeing that eg-switch exists, I wonder if just s/switch-branch/switch/\n> makes more sense.\n>\n> Assuming greenfield development (which we definitely don't have), I\n> don't like the \"restore-files\" name, but the alternative that makes\n> sense is \"checkout\". Then this \"--from\" argument could become \"git\n> checkout-tree <treeish> -- <pathspec>\", and we'd have:\n>\n>     git switch <branchish>\n>     git checkout <pathspec>\n>     git checkout-tree <treeish> -- <pathspec>\n>\n> Or maybe that sucks, anyway what I was going to suggest is *if* others\n> think that made sense as a \"if we designed git today\" endgame whether we\n> could have an opt-in setting where you set e.g. core.uiVersion=3 (in\n> anticipation of Git 3.0) and you'd get that behavior. There could be\n> some other setting where core.uiVersion would use the old behavior (or\n> with another setting, only warn) if we weren't connected to a terminal.\n>\n> I.e. I'm thinking of this as step #2 in a #3 step series. Where this is\n> the fully backwards compatible UI improvement, but someone who'd\n> e.g. use EasyGit or didn't have backwards compatibility concerns could\n> enable step #3 and opt-in to a mode where we'd fixed a bunch of UI warts\n> in a backwards-incompatible way.\n>\n> What would that mode look like? I'd to work on piling that on top of\n> this :)\n\n(Digging some more)\n\nThere's also more interesting prior art at https://gitless.com/ (CC'd\nauthors) and 2x research papers linked at the bottom of that page which\nwere briefly discussed on-list before:\nhttps://public-inbox.org/git/20160930191413.002049b94b3908b15881b77f@domain007.com/\n\nThe \"gitless\" UI has a \"gl checkout\" which just takes paths as I was\nmusing about above (and that Redfin post also suggests).\n"},{"id":"364372","messageId":"DD1074BC-3745-4B86-8BDA-497110130BAD@fabulich.com","threadId":"49793","inReplyTo":"87pnunxz5i.fsf@evledraar.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Dan Fabulich","fromEmail":"dan@fabulich.com","sentAt":"2018-11-29T23:37:26Z","receivedAt":"2018-11-29T23:37:41Z","isPatch":true,"sender":{"key":"dan@fabulich.com","avatar":"https://gravatar.com/avatar/884cee2af623b7675bebdd7b70b13dcb8423150d8f137ec0841859b80a21cd05?d=mp&s=160"},"body":"Assuming the great day has come to think about this, one thing I'd love to do is to unify the name of the index/stage/cache in command-line parameters and the documentation.\n\nThe index/stage/cache should have one canonical name, and the documentation should support that consistently. My taste is to call it the \"stage,\" but references to --index, --keep-index, --no-index, etc. are all over the code, as well as legacy references to \"--cached\".\n\n* You can 'git rm --cached myfile' but you can't 'git rm --staged myfile' or 'git rm --index myfile'.\n\n* You can 'git diff --no-index' or you can 'git diff --cached' with 'git diff --staged' as a synonym, deprioritized in the documentation (\"--staged is a synonym of --cached\"). But you can't 'git diff --index' or 'git diff --no-stage' or 'git diff --no-cache'.\n\n* You can 'git stage' but 'git help stage' is only one line long, declaring that it's a synonym for 'git add'. 'add' appears in the 'git help' common commands, but not 'git stage'. There's no built-in 'git unstage', and certainly no appetite for 'git index' or 'git cache'.\n\n* Not to mention all of the plumbing commands: checkout-index, diff-index, index-pack, merge-index, show-index, and update-index.\n\nMy understanding based on historical threads is that changes like this would be unwelcome, even just to add synonyms and standardize the documentation around \"stage\" as the term (leaving the plumbing commands alone), but if documentation+synonym patches are welcome here, I'd be very enthusiastic.\n\n-Dan\n\n> On Nov 29, 2018, at 3:05 PM, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n> \n> \n> On Thu, Nov 29 2018, Nguyễn Thái Ngọc Duy wrote:\n> \n>> v3 sees switch-branch go back to switch-branch (in v2 it was\n>> checkout-branch). checkout-files is also renamed restore-files (v1 was\n>> restore-paths). Hopefully we won't see another rename.\n>> \n>> I'll try to summarize the differences between the new commands and\n>> 'git checkout' down here, but you're welcome to just head to 07/14 and\n>> read the new man pages.\n>> \n>> 'git switch-branch'\n>> \n>> - does not \"do nothing\", you have to either switch branch, create a\n>>  new branch, or detach. \"git switch-branch\" with no arguments is\n>>  rejected.\n>> \n>> - implicit detaching is rejected. If you need to detach, you need to\n>>  give --detach. Or stick to 'git checkout'.\n>> \n>> - -b/-B is renamed to -c/-C with long option names\n>> \n>> - of course does not accept pathspec\n>> \n>> 'git restore-files'\n>> \n>> - takes a ref from --from argument, not as a free ref. As a result,\n>>  '--' is no longer needed. All non-option arguments are pathspec\n>> \n>> - pathspec is mandatory, you can't do \"git restore-files\" without any\n>>  pathspec.\n>> \n>> - I just remember -p which is allowed to take no pathspec :( I'll fix\n>>  it later.\n>> \n>> - Two more fancy features (the \"git checkout --index\" being the\n>>  default mode and the backup log for accidental overwrites) are of\n>>  course still missing. But they are coming.\n>> \n>> I did not go replace \"detached HEAD\" with \"unnamed branch\" (or \"no\n>> branch\") everywhere because I think a unique term is still good to\n>> refer to this concept. Or maybe \"no branch\" is good enough. I dunno.\n> \n> I finally tracked down\n> https://redfin.engineering/two-commits-that-wrecked-the-user-experience-of-git-f0075b77eab1\n> which I'd remembered reading and couldn't find again in these\n> discussions. Re-reading it while one may not 100% agree with the\n> author's opinion, it's an interesting rabbit hole.\n> \n> I also didn't know about EasyGit, or that Elijah Newren had written\n> it. I haven't seen him chime in on this series, and would be interested\n> to see what he thinks about it.\n> \n> Re the naming question in\n> https://public-inbox.org/git/87o9abzv46.fsf@evledraar.gmail.com/ &\n> seeing that eg-switch exists, I wonder if just s/switch-branch/switch/\n> makes more sense.\n> \n> Assuming greenfield development (which we definitely don't have), I\n> don't like the \"restore-files\" name, but the alternative that makes\n> sense is \"checkout\". Then this \"--from\" argument could become \"git\n> checkout-tree <treeish> -- <pathspec>\", and we'd have:\n> \n>    git switch <branchish>\n>    git checkout <pathspec>\n>    git checkout-tree <treeish> -- <pathspec>\n> \n> Or maybe that sucks, anyway what I was going to suggest is *if* others\n> think that made sense as a \"if we designed git today\" endgame whether we\n> could have an opt-in setting where you set e.g. core.uiVersion=3 (in\n> anticipation of Git 3.0) and you'd get that behavior. There could be\n> some other setting where core.uiVersion would use the old behavior (or\n> with another setting, only warn) if we weren't connected to a terminal.\n> \n> I.e. I'm thinking of this as step #2 in a #3 step series. Where this is\n> the fully backwards compatible UI improvement, but someone who'd\n> e.g. use EasyGit or didn't have backwards compatibility concerns could\n> enable step #3 and opt-in to a mode where we'd fixed a bunch of UI warts\n> in a backwards-incompatible way.\n> \n> What would that mode look like? I'd to work on piling that on top of\n> this :)\n\n"},{"id":"364373","messageId":"340837B7-3004-44F7-9A30-BD3A26876D76@fabulich.com","threadId":"49793","inReplyTo":"87pnunxz5i.fsf@evledraar.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Dan Fabulich","fromEmail":"dan@fabulich.com","sentAt":"2018-11-30T00:16:18Z","receivedAt":"2018-11-30T00:16:27Z","isPatch":true,"sender":{"key":"dan@fabulich.com","avatar":"https://gravatar.com/avatar/884cee2af623b7675bebdd7b70b13dcb8423150d8f137ec0841859b80a21cd05?d=mp&s=160"},"body":"Other thoughts on a global UI rethink:\n\nOne of the most common complaints I hear about git is the conceptual difficulty required in undoing changes. https://ohshitgit.com/\n\n> Git is hard: screwing up is easy, and figuring out how to fix your mistakes is fucking impossible. Git documentation has this chicken and egg problem where you can't search for how to get yourself out of a mess, unless you *already know the name of the thing you need to know about* in order to fix your problem.\n\nA significant fraction of the top-voted questions on StackOverflow are about undoing changes. https://stackoverflow.com/questions/tagged/git\n\nWhat if there were a 'git undo' command that could unify the answers to all of these questions?\n\ngit undo stage\ngit undo rm\ngit undo edit (checkout files from the stage)\n\ngit undo commit (prompt the user whether to revert or reset)\ngit undo reset\ngit undo checkout\n\ngit undo merge\ngit undo pull\ngit undo push (prompt the user whether to force push back to the past or whether to revert pushed commits)\ngit undo rebase\n\ngit undo undo\n\ngit undo clean\ngit undo delete-branch\ngit undo delete-stash\n\nSome of these would be trivial effort, but a lot of them would require fundamental changes in the way git operates. (You can't undo a clean right now because the files are just destroyed.)\n\n-Dan\n\n> On Nov 29, 2018, at 3:05 PM, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n> \n> \n> On Thu, Nov 29 2018, Nguyễn Thái Ngọc Duy wrote:\n> \n>> v3 sees switch-branch go back to switch-branch (in v2 it was\n>> checkout-branch). checkout-files is also renamed restore-files (v1 was\n>> restore-paths). Hopefully we won't see another rename.\n>> \n>> I'll try to summarize the differences between the new commands and\n>> 'git checkout' down here, but you're welcome to just head to 07/14 and\n>> read the new man pages.\n>> \n>> 'git switch-branch'\n>> \n>> - does not \"do nothing\", you have to either switch branch, create a\n>>  new branch, or detach. \"git switch-branch\" with no arguments is\n>>  rejected.\n>> \n>> - implicit detaching is rejected. If you need to detach, you need to\n>>  give --detach. Or stick to 'git checkout'.\n>> \n>> - -b/-B is renamed to -c/-C with long option names\n>> \n>> - of course does not accept pathspec\n>> \n>> 'git restore-files'\n>> \n>> - takes a ref from --from argument, not as a free ref. As a result,\n>>  '--' is no longer needed. All non-option arguments are pathspec\n>> \n>> - pathspec is mandatory, you can't do \"git restore-files\" without any\n>>  pathspec.\n>> \n>> - I just remember -p which is allowed to take no pathspec :( I'll fix\n>>  it later.\n>> \n>> - Two more fancy features (the \"git checkout --index\" being the\n>>  default mode and the backup log for accidental overwrites) are of\n>>  course still missing. But they are coming.\n>> \n>> I did not go replace \"detached HEAD\" with \"unnamed branch\" (or \"no\n>> branch\") everywhere because I think a unique term is still good to\n>> refer to this concept. Or maybe \"no branch\" is good enough. I dunno.\n> \n> I finally tracked down\n> https://redfin.engineering/two-commits-that-wrecked-the-user-experience-of-git-f0075b77eab1\n> which I'd remembered reading and couldn't find again in these\n> discussions. Re-reading it while one may not 100% agree with the\n> author's opinion, it's an interesting rabbit hole.\n> \n> I also didn't know about EasyGit, or that Elijah Newren had written\n> it. I haven't seen him chime in on this series, and would be interested\n> to see what he thinks about it.\n> \n> Re the naming question in\n> https://public-inbox.org/git/87o9abzv46.fsf@evledraar.gmail.com/ &\n> seeing that eg-switch exists, I wonder if just s/switch-branch/switch/\n> makes more sense.\n> \n> Assuming greenfield development (which we definitely don't have), I\n> don't like the \"restore-files\" name, but the alternative that makes\n> sense is \"checkout\". Then this \"--from\" argument could become \"git\n> checkout-tree <treeish> -- <pathspec>\", and we'd have:\n> \n>    git switch <branchish>\n>    git checkout <pathspec>\n>    git checkout-tree <treeish> -- <pathspec>\n> \n> Or maybe that sucks, anyway what I was going to suggest is *if* others\n> think that made sense as a \"if we designed git today\" endgame whether we\n> could have an opt-in setting where you set e.g. core.uiVersion=3 (in\n> anticipation of Git 3.0) and you'd get that behavior. There could be\n> some other setting where core.uiVersion would use the old behavior (or\n> with another setting, only warn) if we weren't connected to a terminal.\n> \n> I.e. I'm thinking of this as step #2 in a #3 step series. Where this is\n> the fully backwards compatible UI improvement, but someone who'd\n> e.g. use EasyGit or didn't have backwards compatibility concerns could\n> enable step #3 and opt-in to a mode where we'd fixed a bunch of UI warts\n> in a backwards-incompatible way.\n> \n> What would that mode look like? I'd to work on piling that on top of\n> this :)\n\n"},{"id":"364376","messageId":"xmqqk1kvmj3k.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CACsJy8Cv9ZwWEs-wsOtms3JCXo7x8fL_PBatcb0TgvrrQuMUdg@mail.gmail.com","subject":"Re: [PATCH/RFC v2 0/7] Introduce new commands switch-branch and checkout-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-30T01:47:59Z","receivedAt":"2018-11-30T01:48:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Wed, Nov 28, 2018 at 9:01 PM Duy Nguyen <pclouds@gmail.com> wrote:\n>> should we do\n>> something about detached HEAD in this switch-branch command (or\n>> whatever its name will be)?\n>>\n>> This is usually a confusing concept to new users\n>\n> And it just occurred to me that perhaps we should call this \"unnamed\n> branch\" (at least at high UI level) instead of detached HEAD. It is\n> technically not as accurate, but much better to understand.\n\nAs I said elsewhere in nearby thread, I agree that \"unnamed branch\"\nis a reasonable way to explain what the state the user is in.  It is\nnot incorrect per-se that HEAD is detached from anything in refs/ in\nsuch a state, but that is an implementation detail of how the\nworktree gets on the unnamed branch (which lasts until the worktree\nnext gets on a named branch, at which point the unnamed branch\ndisappears).\n\n"},{"id":"364377","messageId":"xmqqefb3mhrs.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-30T02:16:39Z","receivedAt":"2018-11-30T02:16:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> 'git switch-branch'\n>\n> - implicit detaching is rejected. If you need to detach, you need to\n>   give --detach. Or stick to 'git checkout'.\n\nOK.  Is \"auto-vivify the named branch based on a remote-tracking\"\nalso rejected, as it is a confusing behaviour that is a too subtle\nand implicit, just like the detaching head is, and require --guess\nor sticking to 'git checkout'?  I think it should.\n\n> - -b/-B is renamed to -c/-C with long option names\n\nI did not expect that these two are the only options that would be\nout of place with the command name split, but presumably you looked\nat all options for both of the two new commands to see if they made\nsense in the new context?\n\n> 'git restore-files'\n>\n> - takes a ref from --from argument, not as a free ref. As a result,\n>   '--' is no longer needed. All non-option arguments are pathspec\n\nOK.  That does make things easier to teach, as there is no need for\ndisambiguation.\n\n> - pathspec is mandatory, you can't do \"git restore-files\" without any\n>   pathspec.\n>\n> - I just remember -p which is allowed to take no pathspec :( I'll fix\n>   it later.\n\nOr leave it out of restore-files as a more advanced feature (just\nlike detaching with HEAD^0 is left out of switch-branch) that the\nuser can stick to 'git checkout' to use.\n\n> - Two more fancy features (the \"git checkout --index\" being the\n>   default mode and the backup log for accidental overwrites) are of\n>   course still missing. But they are coming.\n\nI am unsure about the wisdom of calling it \"--index\", though.\n\nThe \"--index\" option is \"the command can work only on the index, or\nonly on the working tree files, or on both the index and the working\ntree files, and this option tells it to work in the 'both the index\nand the working tree files' mode\", but when \"restore-files\" touches\npaths, it always modifies both the index and the working tree, so\nthe \"--index\" option does not capture the differences well in this\ncontext [*1*].  As I saw this was described as \"not using the usual\n'overlay' semantics [*2*]\", perhaps --overlay/--no-overlay option\nthat defaults to --no-overlay is easier to explain.\n\n    side note 1.  I think the original mention of \"--index\" came in\n    the context of contrasting \"git reset\" with \"git checkout\".\n    \"git reset (--hard|--mixed) -- <pathspec>\" (that does not move\n    HEAD), which does not but perhaps should exist, is very much\n    like \"git checkout -- <pathspec>\", and if \"reset\" were written\n    after the \"--index/--cached\" convention was established, \"reset\n    --hard\" would have called \"reset --index\" while \"reset --mixed\"\n    would have been \"reset --cached\" (i.e. only work on the index\n    and not on the working tree).  And \"reset --index <directory>\"\n    would have worked by removing paths in <directory> that are not\n    in the HEAD and updating paths in <directory> that are in the\n    HEAD, i.e. identical to the non overlay behaviour proposed for\n    the \"git checkout\" command.  So calling the non overlay mode\n    \"--index\" makes sense in the context of discussing \"git reset\",\n    and not in the context of \"git checkout\".\n\n    side note 2.  \"git checkout <tree-ish> <pathspec>\" grabs entries\n    from the <tree-ish> that patch <pathspec> and adds them to the\n    index and checks them out to the working tree.  If the original\n    index has entries that match <pathspec> but do not appear in\n    <tree-ish>, they are left in the result.  That is \"overlaying\n    what was taken from the <tree-ish> on top of what is in the\n    index\".\n\nHaving said all that, I will not be looking at the series until 2.20\nfinal.  And I hope more people do the same to concentrate on helping\nus prevent the last minute glitch slipping in the final release.\n\nThanks.\n"},{"id":"364381","messageId":"CACsJy8DCKQctO+rf=xP5gVVUy9XBq35Z1xSbfwB30nDJMMJsrA@mail.gmail.com","threadId":"49793","inReplyTo":"87pnunxz5i.fsf@evledraar.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-30T05:37:06Z","receivedAt":"2018-11-30T05:37:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Nov 30, 2018 at 12:05 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Assuming greenfield development (which we definitely don't have), I\n> don't like the \"restore-files\" name, but the alternative that makes\n> sense is \"checkout\". Then this \"--from\" argument could become \"git\n> checkout-tree <treeish> -- <pathspec>\", and we'd have:\n>\n>     git switch <branchish>\n>     git checkout <pathspec>\n>     git checkout-tree <treeish> -- <pathspec>\n>\n> Or maybe that sucks, anyway what I was going to suggest is *if* others\n> think that made sense as a \"if we designed git today\" endgame whether we\n> could have an opt-in setting where you set e.g. core.uiVersion=3 (in\n> anticipation of Git 3.0) and you'd get that behavior. There could be\n> some other setting where core.uiVersion would use the old behavior (or\n> with another setting, only warn) if we weren't connected to a terminal.\n\ncore.uiVersion is a big no no to me. I don't want to go to someone's\nterminal, type something and have a total surprise because they set\ndifferent ui version. If you want a total UI redesign, go with a new\nprefix, like \"ng\" (for new git) or something instead of \"git\".\n-- \nDuy\n"},{"id":"364382","messageId":"CACsJy8BZ7s2TbqiO+hensOF0quz+N3h5+GwKqiNTakGaGJ2yeA@mail.gmail.com","threadId":"49793","inReplyTo":"xmqqefb3mhrs.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-30T05:41:45Z","receivedAt":"2018-11-30T05:42:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Nov 30, 2018 at 3:16 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n> > 'git switch-branch'\n> >\n> > - implicit detaching is rejected. If you need to detach, you need to\n> >   give --detach. Or stick to 'git checkout'.\n>\n> OK.  Is \"auto-vivify the named branch based on a remote-tracking\"\n> also rejected, as it is a confusing behaviour that is a too subtle\n> and implicit, just like the detaching head is, and require --guess\n> or sticking to 'git checkout'?  I think it should.\n\nThis touches the \"remote\" concept which I think is another confusing\nthing for new people (your \"master\" is not the same as the server's\n\"master\", aka origin/master) and perhaps this dwim thing helps.\nFrankly I don't do dwim much so I don't know if it's that often used.\n\n> > - -b/-B is renamed to -c/-C with long option names\n>\n> I did not expect that these two are the only options that would be\n> out of place with the command name split, but presumably you looked\n> at all options for both of the two new commands to see if they made\n> sense in the new context?\n\nYeah (at least the description in struct option[] array)\n--\nDuy\n"},{"id":"364383","messageId":"xmqqr2f3kqpi.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CACsJy8BZ7s2TbqiO+hensOF0quz+N3h5+GwKqiNTakGaGJ2yeA@mail.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-30T06:46:33Z","receivedAt":"2018-11-30T06:46:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>>\n>> OK.  Is \"auto-vivify the named branch based on a remote-tracking\"\n>> also rejected, as it is a confusing behaviour that is a too subtle\n>> and implicit, just like the detaching head is, and require --guess\n>> or sticking to 'git checkout'?  I think it should.\n>\n> This touches the \"remote\" concept which I think is another confusing\n> thing for new people (your \"master\" is not the same as the server's\n> \"master\", aka origin/master) and perhaps this dwim thing helps.\n> Frankly I don't do dwim much so I don't know if it's that often used.\n\nI actually think a user who sees a DWIM without understanding what\nthe user wants to do would perceive magic that sometimes works and\nsometimes does not, and some other times it does a random thing that\nthe user does not even understand what is going on.  And such a\nrandom magic that sometimes works, even if the \"sometimes\" is \"most\nof the time\", say 85% of the time, would not help user form the\nright mental model.\n\n\"git checkout master~2\" that DWIMs to \"git checkout --deatch\nmaster~2\", but does totally different thing when \"git checkout\nmaster\" is given, leaving the user confused \"what is so different\nbetween these two?\".  Until the user realizes 'master' can serve\nboth as a branch name and a name for a commit object, while master~2\ncan only be a name for a commit object and is not a branch name, the\nbehaviour of the command will stay to be mysterious and DWIMmage\nwould not help user form the right mental model.  I earlier said\nthat I agree with your decision to leave the implied form out of\nswitch-branch for exactly that reason.  \n\nThe behaviour falls into the same category as \"git checkout frotz\"\nthat DWIMS to \"git checkout -b frotz -t remotes/origin/frotz\", which\nalso is mysterious until the user understands your 'master' is unique\nand is different from 'master' to everybody else, I would think.\n"},{"id":"364384","messageId":"xmqqmuprkqnx.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CACsJy8DCKQctO+rf=xP5gVVUy9XBq35Z1xSbfwB30nDJMMJsrA@mail.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-30T06:47:30Z","receivedAt":"2018-11-30T06:47:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> core.uiVersion is a big no no to me. I don't want to go to someone's\n> terminal, type something and have a total surprise because they set\n> different ui version. If you want a total UI redesign, go with a new\n> prefix, like \"ng\" (for new git) or something instead of \"git\".\n\nYup, very good point to keep in mind.\n"},{"id":"364385","messageId":"CACsJy8Dg06DbbSLuuVHKgQUwHXqqVZLjbmkdkN=m=Vx-QeP6zQ@mail.gmail.com","threadId":"49793","inReplyTo":"340837B7-3004-44F7-9A30-BD3A26876D76@fabulich.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-30T06:49:20Z","receivedAt":"2018-11-30T06:49:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Nov 30, 2018 at 1:16 AM Dan Fabulich <dan@fabulich.com> wrote:\n>\n> Other thoughts on a global UI rethink:\n>\n> One of the most common complaints I hear about git is the conceptual difficulty required in undoing changes. https://ohshitgit.com/\n>\n> > Git is hard: screwing up is easy, and figuring out how to fix your mistakes is fucking impossible. Git documentation has this chicken and egg problem where you can't search for how to get yourself out of a mess, unless you *already know the name of the thing you need to know about* in order to fix your problem.\n>\n> A significant fraction of the top-voted questions on StackOverflow are about undoing changes. https://stackoverflow.com/questions/tagged/git\n>\n> What if there were a 'git undo' command that could unify the answers to all of these questions?\n>\n> git undo stage\n> git undo rm\n> git undo edit (checkout files from the stage)\n>\n> git undo commit (prompt the user whether to revert or reset)\n> git undo reset\n> git undo checkout\n>\n> git undo merge\n> git undo pull\n> git undo push (prompt the user whether to force push back to the past or whether to revert pushed commits)\n> git undo rebase\n>\n> git undo undo\n>\n> git undo clean\n> git undo delete-branch\n> git undo delete-stash\n>\n> Some of these would be trivial effort, but a lot of them would require fundamental changes in the way git operates. (You can't undo a clean right now because the files are just destroyed.)\n\nWe're getting there. The biggest problem I have is how this \"git undo\"\nshould work, not the changes behind to support it. For example, if I\npulled then did some rebase, what would \"git undo pull\" do?\n-- \nDuy\n"},{"id":"364391","messageId":"87zhtqvm66.fsf@evledraar.gmail.com","threadId":"49793","inReplyTo":"CACsJy8DCKQctO+rf=xP5gVVUy9XBq35Z1xSbfwB30nDJMMJsrA@mail.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-11-30T11:29:05Z","receivedAt":"2018-11-30T11:29:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Nov 30 2018, Duy Nguyen wrote:\n\n> On Fri, Nov 30, 2018 at 12:05 AM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>> Assuming greenfield development (which we definitely don't have), I\n>> don't like the \"restore-files\" name, but the alternative that makes\n>> sense is \"checkout\". Then this \"--from\" argument could become \"git\n>> checkout-tree <treeish> -- <pathspec>\", and we'd have:\n>>\n>>     git switch <branchish>\n>>     git checkout <pathspec>\n>>     git checkout-tree <treeish> -- <pathspec>\n>>\n>> Or maybe that sucks, anyway what I was going to suggest is *if* others\n>> think that made sense as a \"if we designed git today\" endgame whether we\n>> could have an opt-in setting where you set e.g. core.uiVersion=3 (in\n>> anticipation of Git 3.0) and you'd get that behavior. There could be\n>> some other setting where core.uiVersion would use the old behavior (or\n>> with another setting, only warn) if we weren't connected to a terminal.\n>\n> core.uiVersion is a big no no to me. I don't want to go to someone's\n> terminal, type something and have a total surprise because they set\n> different ui version. If you want a total UI redesign, go with a new\n> prefix, like \"ng\" (for new git) or something instead of \"git\".\n\nI don't think that's a viable way forward. First, we're not talking\nabout a total change of the UI. Most the main porcelain will stay the\nsame. So we'd have a \"ng\" that's >98% the same UI, and then if we\ndiscover warts in in 10 years we'd like to fix then what do wo do? Ship\n\"nng\" and so forth?\n\nWe already have this UI problem with various config variables that\nchange things. I think we should just solve this general issue by a\ncombination of:\n\n a) Accepting that over the long term we will have Git's UI changing,\n    sometimes in breaking ways (with sensible phase-in), hopefully for\n    the better. E.g. we had this with \"git-init\" v.s. \"git init\". This\n    is similar, there'd be an error due to ambiguity with a \"hint\"\n    saying use the new thing if you e.g. feed \"git checkout\" a branch\n    name.\n\n b) For the general problem of \"user has exotic config\" we should learn\n    a \"git -Q <cmd>\" option similar to Emacs, which is another highly\n    customizable piece of software that has a \"don't read user config\"\n    escape hatch.\n\n    That's a bit more complex than for Emacs since we need to actually\n    read some config (e.g. remote config from .git/config), and maybe\n    opt to keep some stuff like user.*. But there's no reason we can't\n    have such a black/whitelist of config & env variables that impact us\n    with a switch to get \"make it as if nothing was configured\" for\n    whatever sane version of that we'd come up with.\n"},{"id":"364392","messageId":"CACsJy8Cd6OVn9MpcTB=sePZUV-a_h45jkbuTSrS-jasG=2oT4g@mail.gmail.com","threadId":"49793","inReplyTo":"87zhtqvm66.fsf@evledraar.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-30T12:10:23Z","receivedAt":"2018-11-30T12:10:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Nov 30, 2018 at 12:29 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n>\n> On Fri, Nov 30 2018, Duy Nguyen wrote:\n>\n> > On Fri, Nov 30, 2018 at 12:05 AM Ævar Arnfjörð Bjarmason\n> > <avarab@gmail.com> wrote:\n> >> Assuming greenfield development (which we definitely don't have), I\n> >> don't like the \"restore-files\" name, but the alternative that makes\n> >> sense is \"checkout\". Then this \"--from\" argument could become \"git\n> >> checkout-tree <treeish> -- <pathspec>\", and we'd have:\n> >>\n> >>     git switch <branchish>\n> >>     git checkout <pathspec>\n> >>     git checkout-tree <treeish> -- <pathspec>\n> >>\n> >> Or maybe that sucks, anyway what I was going to suggest is *if* others\n> >> think that made sense as a \"if we designed git today\" endgame whether we\n> >> could have an opt-in setting where you set e.g. core.uiVersion=3 (in\n> >> anticipation of Git 3.0) and you'd get that behavior. There could be\n> >> some other setting where core.uiVersion would use the old behavior (or\n> >> with another setting, only warn) if we weren't connected to a terminal.\n> >\n> > core.uiVersion is a big no no to me. I don't want to go to someone's\n> > terminal, type something and have a total surprise because they set\n> > different ui version. If you want a total UI redesign, go with a new\n> > prefix, like \"ng\" (for new git) or something instead of \"git\".\n>\n> I don't think that's a viable way forward. First, we're not talking\n> about a total change of the UI. Most the main porcelain will stay the\n> same. So we'd have a \"ng\" that's >98% the same UI, and then if we\n> discover warts in in 10 years we'd like to fix then what do wo do? Ship\n> \"nng\" and so forth?\n\nYes. So think it through and try not to do it often.\n\n> We already have this UI problem with various config variables that\n> change things. I think we should just solve this general issue by a\n> combination of:\n>\n>  a) Accepting that over the long term we will have Git's UI changing,\n>     sometimes in breaking ways (with sensible phase-in), hopefully for\n>     the better. E.g. we had this with \"git-init\" v.s. \"git init\". This\n>     is similar, there'd be an error due to ambiguity with a \"hint\"\n>     saying use the new thing if you e.g. feed \"git checkout\" a branch\n>     name.\n\nAnd I hate adding a zillion of config keys to change little things\nlike this. Now you use this to change bigger behavior. No.\n-- \nDuy\n"},{"id":"364452","messageId":"20181202185838.GN4883@hank.intra.tgummerer.com","threadId":"49793","inReplyTo":"xmqqefb3mhrs.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2018-12-02T18:58:38Z","receivedAt":"2018-12-02T18:58:44Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 11/30, Junio C Hamano wrote:\n> \n> I am unsure about the wisdom of calling it \"--index\", though.\n> \n> The \"--index\" option is \"the command can work only on the index, or\n> only on the working tree files, or on both the index and the working\n> tree files, and this option tells it to work in the 'both the index\n> and the working tree files' mode\", but when \"restore-files\" touches\n> paths, it always modifies both the index and the working tree, so\n> the \"--index\" option does not capture the differences well in this\n> context [*1*].  As I saw this was described as \"not using the usual\n> 'overlay' semantics [*2*]\", perhaps --overlay/--no-overlay option\n> that defaults to --no-overlay is easier to explain.\n\nAgreed, I think --{no-,}overlay is a much better name for the option,\nI'll use that for my patch series (I hope to send that soon after 2.20\nis released).\n\nI must admit that I was not aware that the mode is called overlay\nmode, before you explained it to me, so I wouldn't expect most users\nto know either.  But as it's easy to explain that probably doesn't\nmatter much.\n\n>     side note 1.  I think the original mention of \"--index\" came in\n>     the context of contrasting \"git reset\" with \"git checkout\".\n>     \"git reset (--hard|--mixed) -- <pathspec>\" (that does not move\n>     HEAD), which does not but perhaps should exist, is very much\n>     like \"git checkout -- <pathspec>\", and if \"reset\" were written\n>     after the \"--index/--cached\" convention was established, \"reset\n>     --hard\" would have called \"reset --index\" while \"reset --mixed\"\n>     would have been \"reset --cached\" (i.e. only work on the index\n>     and not on the working tree).  And \"reset --index <directory>\"\n>     would have worked by removing paths in <directory> that are not\n>     in the HEAD and updating paths in <directory> that are in the\n>     HEAD, i.e. identical to the non overlay behaviour proposed for\n>     the \"git checkout\" command.  So calling the non overlay mode\n>     \"--index\" makes sense in the context of discussing \"git reset\",\n>     and not in the context of \"git checkout\".\n> \n>     side note 2.  \"git checkout <tree-ish> <pathspec>\" grabs entries\n>     from the <tree-ish> that patch <pathspec> and adds them to the\n>     index and checks them out to the working tree.  If the original\n>     index has entries that match <pathspec> but do not appear in\n>     <tree-ish>, they are left in the result.  That is \"overlaying\n>     what was taken from the <tree-ish> on top of what is in the\n>     index\".\n> \n> Having said all that, I will not be looking at the series until 2.20\n> final.  And I hope more people do the same to concentrate on helping\n> us prevent the last minute glitch slipping in the final release.\n> \n> Thanks.\n"},{"id":"364456","messageId":"xmqqbm63iuf0.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"20181202185838.GN4883@hank.intra.tgummerer.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-12-02T19:46:11Z","receivedAt":"2018-12-02T19:46:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n> Agreed, I think --{no-,}overlay is a much better name for the option,\n> I'll use that for my patch series (I hope to send that soon after 2.20\n> is released).\n\nOK.\n\n> I must admit that I was not aware that the mode is called overlay\n> mode, before you explained it to me, so I wouldn't expect most users\n> to know either.  But as it's easy to explain that probably doesn't\n> matter much.\n\nI do not think \"the mode is called the overlay mode\" is so accurate\na description.  I think I've seen the word 'overlay' used to\ndescribe the behaviour in earlier discussions, but because there is\nno 'non-overlay' mode exists in versions of 'git checkout' the\nend-users have, the users won't even be aware of the possibility\nthat mode different from what they are used to see could exist, or\nthat the mode that they are used to see could be called/explained as\nthe 'overlay' mode.  IOW, we should pick the best phrase to explain\nthe behaviour we can use when coming up with the command line\noption, and that phrase does not have to be 'overlay'---there is no\n\"using the word 'overlay' for this is good because the users are\nfamiliar with the existing use of the word\", simply because there\nisn't such familiarilty ;-)\n"},{"id":"364490","messageId":"CAGZ79kZk-im9=dgMJof2LGuR6hftMcnwx0=G-sjhkERWDqXEwA@mail.gmail.com","threadId":"49793","inReplyTo":"CACsJy8CkBV48Yd9FHfLVQSHJ630uw8icn128xjAPUOeWJVWfVA@mail.gmail.com","subject":"Re: [PATCH/RFC v2 0/7] Introduce new commands switch-branch and checkout-files","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-12-03T21:42:59Z","receivedAt":"2018-12-03T21:43:14Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Nov 29, 2018 at 7:33 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Wed, Nov 28, 2018 at 9:30 PM Stefan Beller <sbeller@google.com> wrote:\n> >\n> > On Wed, Nov 28, 2018 at 12:09 PM Duy Nguyen <pclouds@gmail.com> wrote:\n> > >\n> > > On Wed, Nov 28, 2018 at 9:01 PM Duy Nguyen <pclouds@gmail.com> wrote:\n> > > > should we do\n> > > > something about detached HEAD in this switch-branch command (or\n> > > > whatever its name will be)?\n> > > >\n> > > > This is usually a confusing concept to new users\n> > >\n> > > And it just occurred to me that perhaps we should call this \"unnamed\n> > > branch\" (at least at high UI level) instead of detached HEAD. It is\n> > > technically not as accurate, but much better to understand.\n> >\n> > or 'direct' branch?\n>\n> makes me think, what is an indirect branch?\n\nI drew the term from HEAD pointing to a branch pointing\nto a commit, i.e. HEAD indirectly points to a commit, but\nin 'direct' branch mode, HEAD contains the commit id.\n\nSo indirect branch would work for our current 'real' branches.\n\nWhen asked out of context of this discussion, I might add\nyet another layer of abstraction to make an 'indirect branch',\ni.e. HEAD pointing to a symbolic ref that points at a branch\nthat points to a commit.\n\nThe term symref is what we currently use\n(Just looked into gitglossary, where we distinguish\nsymbolic refs from pseudorefs) for hat I would have called\nan indirect branch as well.\n\nSo maybe we need to measure the level of indirection\n(\"How often do we need to dereference the ref/object to get\na commit oid?\") to come to terms in how to describe\nthe use cases easily.\n\nHere is a fun-one:\n  git checkout <symbolic-ref>\n  git checkout --detach\n\nCurrently the --detach option detaches HEAD from\nbranch pointing at the object id, i.e. it is the same as\n  git checkout <oid>\n\nwhereas when we focus on the levels of indirection\nit would also be reasonable to have\n  git checkout <branch>\nas a reasonable alternative, where <branch> is the\nbranch that is pointed at from the <symbolic ref>.\n"},{"id":"364514","messageId":"CABPp-BHQ68pkvO8yXYuy=0D6ne8u=5CUMDqiN0jtRrxCL55n2g@mail.gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-8-pclouds@gmail.com","subject":"Re: [PATCH v3 07/14] checkout: split into switch-branch and restore-files","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2018-12-04T00:45:08Z","receivedAt":"2018-12-04T00:45:24Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Nov 29, 2018 at 2:03 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> \"git checkout\" doing too many things is a source of confusion for many\n> users (and it even bites old timers sometimes). To rememdy that, the\n> command is now split in two: switch-branch and checkout-files. The\n\n\"checkout-files\" here....(will comment more on this below)\n\n> good old \"git checkout\" command is still here and will be until all\n> (or most of users) are sick of it.\n>\n> See the new man pages for the final design of these commands. The\n> actual implementation though is still pretty much the same as \"git\n> checkout\". Following patches will adjust their behavior to match the\n> man pages.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  .gitignore                          |   2 +\n>  Documentation/git-checkout.txt      |   5 +\n>  Documentation/git-restore-files.txt | 167 ++++++++++++++++\n>  Documentation/git-switch-branch.txt | 289 ++++++++++++++++++++++++++++\n>  Makefile                            |   2 +\n>  builtin.h                           |   2 +\n>  builtin/checkout.c                  |  84 ++++++--\n>  command-list.txt                    |   2 +\n>  git.c                               |   2 +\n>  9 files changed, 543 insertions(+), 12 deletions(-)\n>  create mode 100644 Documentation/git-restore-files.txt\n>  create mode 100644 Documentation/git-switch-branch.txt\n>\n> diff --git a/.gitignore b/.gitignore\n> index 0d77ea5894..c63dcb1427 100644\n> --- a/.gitignore\n> +++ b/.gitignore\n> @@ -143,6 +143,7 @@\n>  /git-request-pull\n>  /git-rerere\n>  /git-reset\n> +/git-restore-files\n\n...and \"restore-files\" here.  Should be consistent with whatever name you pick.\n\n>  /git-rev-list\n>  /git-rev-parse\n>  /git-revert\n> @@ -167,6 +168,7 @@\n>  /git-submodule\n>  /git-submodule--helper\n>  /git-svn\n> +/git-switch-branch\n>  /git-symbolic-ref\n>  /git-tag\n>  /git-unpack-file\n> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\n> index 25887a6087..25ec7f508f 100644\n> --- a/Documentation/git-checkout.txt\n> +++ b/Documentation/git-checkout.txt\n> @@ -406,6 +406,11 @@ $ edit frotz\n>  $ git add frotz\n>  ------------\n>\n> +SEE ALSO\n> +--------\n> +linkgit:git-switch-branch[1]\n> +linkgit:git-restore-files[1]\n> +\n>  GIT\n>  ---\n>  Part of the linkgit:git[1] suite\n> diff --git a/Documentation/git-restore-files.txt b/Documentation/git-restore-files.txt\n> new file mode 100644\n> index 0000000000..03c1250ad0\n> --- /dev/null\n> +++ b/Documentation/git-restore-files.txt\n> @@ -0,0 +1,167 @@\n> +git-restore-files(1)\n> +====================\n> +\n> +NAME\n> +----\n> +git-restore-files - Restore working tree files\n> +\n> +SYNOPSIS\n> +--------\n> +[verse]\n> +'git restore-files' [-f|--ours|--theirs|-m|--conflict=<style>] [--from=<tree-ish>] <pathspec>...\n\nSuggesting that you can use both --ours and --from?  Or -f and --from?\n That seems bad; see below for more on this...\n\n> +'git restore-files' [--from=<tree-ish>] <pathspec>...\n\nIsn't this already inferred by the previous line?  Or was the\ninclusion of --from on the previous line in error?  Looking at the\ngit-checkout manpage, it looks like you may have just been copying an\nexisting weirdness, but it needs to be fixed.  ;-)\n\n> +'git restore-files' (-p|--patch) [--from=<tree-ish>] [<pathspec>...]\n> +\n> +DESCRIPTION\n> +-----------\n> +Updates files in the working tree to match the version in the index\n> +or the specified tree.\n> +\n> +'git restore-files' [--from=<tree-ish>] <pathspec>...::\n\n<tree-ish> and <pathspec>?  I understand <commit-ish> and <pathspec>,\nor <tree-ish> but have no clue why it'd be okay to specify <tree-ish>\nand <pathspec> together.  What does that even mean?\n\nAlso, rather than fixing from <tree-ish> to <commit-ish> or <commit>,\nperhaps we should just use <revision> here?  (I'm thinking of git\nrev-parse's \"Specifying revisions\", which suggests \"revisions\" as a\ngood substitute for \"commit-ish\" that isn't quite so weird for new\nusers.)\n\n> +\n> +       Overwrite paths in the working tree by replacing with the\n> +       contents in the index or in the <tree-ish> (most often a\n> +       commit).  When a <tree-ish> is given, the paths that\n> +       match the <pathspec> are updated both in the index and in\n> +       the working tree.\n\nIs that the default we really want for this command?  Why do we\nautomatically assume these files are ready for commit?  I understand\nthat it's what checkout did, but I'd find it more natural to only\naffect the working tree by default.  We can give it an option for\naffecting the index instead (or perhaps in addition).\n\n> ++\n> +The index may contain unmerged entries because of a previous failed merge.\n> +By default, if you try to check out such an entry from the index, the\n> +checkout operation will fail and nothing will be checked out.\n> +Using `-f` will ignore these unmerged entries.  The contents from a\n> +specific side of the merge can be checked out of the index by\n> +using `--ours` or `--theirs`.  With `-m`, changes made to the working tree\n> +file can be discarded to re-create the original conflicted merge result.\n> +\n> +'git restore-files' (-p|--patch) [--from=<tree-ish>] [<pathspec>...]::\n> +       This is similar to the \"check out paths to the working tree\n> +       from either the index or from a tree-ish\" mode described\n> +       above, but lets you use the interactive interface to show\n> +       the \"diff\" output and choose which hunks to use in the\n> +       result.  See below for the description of `--patch` option.\n> +\n> +OPTIONS\n> +-------\n> +-q::\n> +--quiet::\n> +       Quiet, suppress feedback messages.\n> +\n> +--[no-]progress::\n> +       Progress status is reported on the standard error stream\n> +       by default when it is attached to a terminal, unless `--quiet`\n> +       is specified. This flag enables progress reporting even if not\n> +       attached to a terminal, regardless of `--quiet`.\n> +\n> +-f::\n> +--force::\n> +       Do not fail upon unmerged entries; instead, unmerged entries\n> +       are ignored.\n\nYou just copied this from the checkout manpage, but this is ambiguous\nand/or wrong; using git-checkout (since your patch-series doesn't\napply cleanly to either master or next for me):\n\n$ sha1sum counting\nc0ed0e34b0fbef4274ef59480e0a0a1cb2776870  counting\n$ git checkout counting; echo $?\nerror: path 'counting' is unmerged\n1\n$ git checkout -f counting; echo $?\nwarning: path 'counting' is unmerged\n0\n$ sha1sum counting\nc0ed0e34b0fbef4274ef59480e0a0a1cb2776870  counting\n\nMaybe printing a warning counts as \"ignored\", though it doesn't seem\nlike it.  However, even worse is:\n\n$ git checkout -f HEAD~1 counting\n$ sha1sum counting\n612ca68d0305c821750a452e9d5bf050e915824f  counting\n\nNow the unmerged entry wasn't ignored; it was updated in the working\ntree and overwritten in the index.\n\n\nPerhaps -f and --from should be incompatible and throw an error if\nboth are specified?  Also does \"printed a warning message for\"\nactually count as \"ignored\" or should the documentation for this\noption be updated?\n\n> +--ours::\n> +--theirs::\n> +       Check out stage #2 ('ours') or #3 ('theirs') for unmerged\n> +       paths.\n> ++\n> +Note that during `git rebase` and `git pull --rebase`, 'ours' and\n> +'theirs' may appear swapped; `--ours` gives the version from the\n> +branch the changes are rebased onto, while `--theirs` gives the\n> +version from the branch that holds your work that is being rebased.\n> ++\n> +This is because `rebase` is used in a workflow that treats the\n> +history at the remote as the shared canonical one, and treats the\n> +work done on the branch you are rebasing as the third-party work to\n> +be integrated, and you are temporarily assuming the role of the\n> +keeper of the canonical history during the rebase.  As the keeper of\n> +the canonical history, you need to view the history from the remote\n> +as `ours` (i.e. \"our shared canonical history\"), while what you did\n> +on your side branch as `theirs` (i.e. \"one contributor's work on top\n> +of it\").\n\nTotal aside because I'm not sure what you could change here, but man\ndo I hate this.\n\n> +\n> +--ignore-skip-worktree-bits::\n> +       In sparse checkout mode, update only entries matched by\n> +       <paths> and sparse patterns in\n> +       $GIT_DIR/info/sparse-checkout. This option ignores the sparse\n> +       patterns and adds back any files in <paths>.\n\nThis doesn't make any sense now that you've removed the \"`git checkout\n-- <paths>` would\" from the original.\n\n> +\n> +-m::\n> +--merge::\n> +       When checking out paths from the index, this option lets you\n> +       recreate the conflicted merge in the specified paths.\n> +\n> +--conflict=<style>::\n> +       The same as --merge option above, but changes the way the\n> +       conflicting hunks are presented, overriding the\n> +       merge.conflictStyle configuration variable.  Possible values are\n> +       \"merge\" (default) and \"diff3\" (in addition to what is shown by\n> +       \"merge\" style, shows the original contents).\n> +\n> +-p::\n> +--patch::\n> +       Interactively select hunks in the difference between the\n> +       <tree-ish> (or the index, if unspecified) and the working\n> +       tree.  The chosen hunks are then applied in reverse to the\n> +       working tree (and if a <tree-ish> was specified, the index).\n> ++\n> +This means that you can use `git restore-files -p` to selectively\n> +discard edits from your current working tree. See the ``Interactive\n> +Mode'' section of linkgit:git-add[1] to learn how to operate the\n> +`--patch` mode.\n> +\n> +--[no-]recurse-submodules::\n> +       Using --recurse-submodules will update the content of all initialized\n> +       submodules according to the commit recorded in the superproject. If\n> +       local modifications in a submodule would be overwritten the checkout\n> +       will fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n> +       is used, the work trees of submodules will not be updated.\n> +       Just like linkgit:git-submodule[1], this will detach the\n> +       submodules HEAD.\n> +\n> +<tree-ish>::\n> +       Tree to checkout from (when paths are given). If not specified,\n> +       the index will be used.\n\nAgain, I'd really rather use <revision> here.\n\n> +\n> +EXAMPLES\n> +--------\n> +\n> +. The following sequence checks out the `master` branch, reverts\n> +the `Makefile` to two revisions back, deletes hello.c by\n> +mistake, and gets it back from the index.\n> ++\n> +------------\n> +$ git switch-branch master                    <1>\n> +$ git restore-files --from master~2 Makefile  <2>\n> +$ rm -f hello.c\n> +$ git restore-files hello.c                   <3>\n> +------------\n> ++\n> +<1> switch branch\n> +<2> take a file out of another commit\n> +<3> restore hello.c from the index\n> ++\n\nWhy is the switch-branch command labelled but not the rm command?  It\nmade sense in the original checkout manpage to label all checkout\ncommands, but here since only restore-files is being discussed it\nseems the switch-branch should lose its label.\n\n> +If you want to check out _all_ C source files out of the index,\n> +you can say\n> ++\n> +------------\n> +$ git restore-files '*.c'\n> +------------\n> ++\n> +Note the quotes around `*.c`.  The file `hello.c` will also be\n> +checked out, even though it is no longer in the working tree,\n> +because the file globbing is used to match entries in the index\n> +(not in the working tree by the shell).\n\nSounds good.\n\n> ++\n> +If you have an unfortunate branch that is named `hello.c`, this\n> +step would be confused as an instruction to switch to that branch.\n> +You should instead write:\n> ++\n> +------------\n> +$ git restore-files hello.c\n> +------------\n\nIsn't the point of this command to allow us to remove paragraphs like\nthis last one?\n\n> +\n> +SEE ALSO\n> +--------\n> +linkgit:git-checkout[1]\n> +\n> +GIT\n> +---\n> +Part of the linkgit:git[1] suite\n\n\nMy single biggest worry about this whole series is that I'm worried\nyou're perpetuating and even further ingraining one of the biggest\nusability problems with checkout: people suggest and use it for\nreverting/restoring paths to a previous version, but it doesn't do\nthat:\n\ngit restore-files --from master~10 Documentation/\n<edit some non-documentation files>\ngit add -u\ngit commit -m \"Rationale for changing files including reverting Documentation/\"\n\nIn particular, now you have a mixture of files in Documentation/ from\nmaster~10 (er, now likely master~11) and HEAD~1; any files and\nsub-directories that existed in HEAD~1 still remain and are mixed with\nall other files in Documentation/ from the older commit.\n\nYou may think this is a special case, but this particular issue\nresults in some pretty bad surprises.  Also, it was pretty surprising\nto me just how difficult it was to implement an svn-like revert in\nEasyGit, in large part because of this 'oversight' in git.  git\ncheckout -- <paths> to me has always been fundamentally wrong, but I\njust wasn't sure if I wanted to fight the backward compatibility\nbattle and suggest changing it.  With a new command, we definitely\nshouldn't be reinforcing this error.  (And maybe we should consider\ntaking the time to fix git checkout too.)\n\n\n> diff --git a/Documentation/git-switch-branch.txt b/Documentation/git-switch-branch.txt\n> new file mode 100644\n> index 0000000000..d5bf5cb37d\n> --- /dev/null\n> +++ b/Documentation/git-switch-branch.txt\n> @@ -0,0 +1,289 @@\n> +git-switch-branch(1)\n> +====================\n> +\n> +NAME\n> +----\n> +git-switch-branch - Switch branches\n> +\n> +SYNOPSIS\n> +--------\n> +[verse]\n> +'git switch-branch' [-q] [-f] [-m] <branch>\n> +'git switch-branch' [-q] [-f] [-m] --detach [<commit>]\n> +'git switch-branch' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]\n\nYou label the options as -b/-B here, but -c/-C below.  Should be consistent.\n\n> +\n> +DESCRIPTION\n> +-----------\n> +Switch to a specified branch and update files in the working tree to\n> +match it.\n> +\n> +'git switch-branch' <branch>::\n> +       To prepare for working on <branch>, switch to it by updating\n> +       the index and the files in the working tree. Local\n> +       modifications to the files in the working tree are kept, so\n> +       that they can be committed to the <branch>.\n> ++\n> +If <branch> is not found but there does exist a tracking branch in\n> +exactly one remote (call it <remote>) with a matching name, treat as\n> +equivalent to\n> ++\n> +------------\n> +$ git switch-branch -b <branch> --track <remote>/<branch>\n> +------------\n\nIf we're making --detach explicit (which I think is good), shouldn't\nthis also be made explicit?  I think I saw Junio arguing for this in\nanother thread.\n\nAlso, this is another case where you used -b instead of -c.  Finally,\n--track wasn't mentioned in the synopsis but it is shown the first\ntime -b or -c is used?\n\n> ++\n> +If the branch exists in multiple remotes and one of them is named by\n> +the `checkout.defaultRemote` configuration variable, we'll use that\n> +one for the purposes of disambiguation, even if the `<branch>` isn't\n> +unique across all remotes. Set it to\n> +e.g. `checkout.defaultRemote=origin` to always checkout remote\n> +branches from there if `<branch>` is ambiguous but exists on the\n> +'origin' remote. See also `checkout.defaultRemote` in\n> +linkgit:git-config[1].\n\nSo switch-branch will be controlled by checkout.* config variables?\nThat probably makes the most sense, but it does dilute the advantage\nof adding these simpler commands.\n\nAlso, the fact that we're trying to make a simpler command makes me\nthink that removing the auto-vivify behavior from the default and\nadding a simple flag which users can pass to request will allow this\npart of the documentation to be hidden behind the appropriate flag,\nwhich may make it easier for users to compartmentalize the command and\nit's options, enabling them to learn as they go.\n\n> +\n> +'git switch-branch' -c|-C <new_branch> [<start_point>]::\n> +\n> +       Specifying `-c` causes a new branch to be created as if\n> +       linkgit:git-branch[1] were called and then switched to. In\n> +       this case you can use the `--track` or `--no-track` options,\n> +       which will be passed to 'git branch'.  As a convenience,\n> +       `--track` without `-c` implies branch creation; see the\n> +       description of `--track` below.\n\nCan we get rid of --track/--no-track and just provide a flag (which\ntakes no arguments) for the user to use?  Problems with --track:\n  * it's not even in your synopsis\n  * user has to repeat themselves (e.g. 'next' in two places from '-c\nnext --track origin/next'); this repetition is BOTH laborious AND\nerror-prone\n  * it's rather inconsistent: --track is the default due to\nauto-vivify when the user specifies nothing but a branch name that\ndoesn't exist yet, but when the user realizes the branch doesn't exist\nyet and asks to have it created then suddenly tracking is not the\ndefault??\n\n\nI'm not sure what's best, but here's some food for thought:\n\n\n   git switch-branch <branch>\nswitches to <branch>, if it exists.  Error cases:\n  * If <branch> isn't actually a branch but a <tag> or\n<remote-tracking-branch> or <revision>, error out and suggest using\n--detach.\n  * If <branch> isn't actually a branch but there is a similarly named\n<remote-tracking-branch> (e.g. origin/<branch>), then suggest using\n-c.\n\n  git switch-branch -c <branch>\ncreates <branch> and, if a relevant-remote-tracking branch exists,\nbase the branch on that revision and set the new branch up to track\nit.  Error cases:\n  * If <branch> already exists, error out, suggesting -C or using a\nnon-conflicting name instead.\n\nOther cases:\n  * user wants a branch named 'master' that tracks 'origin/next'?  Use\ngit branch instead, don't support that in switch-branch.\n  * user wants a branch named 'master' that doesn't track\n'origin/master' despite 'origin/master' existing?  Use git branch\ninstead; don't support that in switch-branch.\n\n> ++\n> +If `-C` is given, <new_branch> is created if it doesn't exist;\n> +otherwise, it is reset. This is the transactional equivalent of\n> ++\n> +------------\n> +$ git branch -f <branch> [<start_point>]\n> +$ git switch-branch <branch>\n> +------------\n> ++\n> +that is to say, the branch is not reset/created unless \"git\n> +switch-branch\" is successful.\n\n...and when exactly would it fail?  Reading this, it looks like the\nonly possible error condition was removed due saying we'll reset the\nbranch if it already exists, so it's rather confusing.\n\n> +\n> +'git switch-branch' --detach [<commit>]::\n> +\n> +       Prepare to work on a unnamed branch on top of <commit> (see\n> +       \"DETACHED HEAD\" section), and updating the index and the files\n> +       in the working tree.  Local modifications to the files in the\n> +       working tree are kept, so that the resulting working tree will\n> +       be the state recorded in the commit plus the local\n> +       modifications.\n> ++\n> +When the <commit> argument is a branch name, the `--detach` option can\n> +be used to detach HEAD at the tip of the branch (`git switch-branch\n> +<branch>` would check out that branch without detaching HEAD).\n> ++\n> +Omitting <commit> detaches HEAD at the tip of the current branch.\n> +\n> +OPTIONS\n> +-------\n> +-q::\n> +--quiet::\n> +       Quiet, suppress feedback messages.\n> +\n> +--[no-]progress::\n> +       Progress status is reported on the standard error stream\n> +       by default when it is attached to a terminal, unless `--quiet`\n> +       is specified. This flag enables progress reporting even if not\n> +       attached to a terminal, regardless of `--quiet`.\n> +\n> +-f::\n> +--force::\n> +       Proceed even if the index or the working tree differs from\n> +       HEAD.  This is used to throw away local changes.\n\nHaven't thought through this thoroughly, but do we really need an\noption for that instead of telling users to 'git reset --hard HEAD'\nbefore switching branches if they want their stuff thrown away?\n\n> +-c <new_branch>::\n> +--create <new_branch>::\n> +       Create a new branch named <new_branch> and start it at\n> +       <start_point>; see linkgit:git-branch[1] for details.\n> +\n> +-C <new_branch>::\n> +--force-create <new_branch>::\n> +       Creates the branch <new_branch> and start it at <start_point>;\n> +       if it already exists, then reset it to <start_point>. This is\n> +       equivalent to running \"git branch\" with \"-f\"; see\n> +       linkgit:git-branch[1] for details.\n\nMakes sense, but let's get the -b/-B vs. -c/-C consistent.\n\n> +\n> +-t::\n> +--track::\n> +       When creating a new branch, set up \"upstream\" configuration. See\n> +       \"--track\" in linkgit:git-branch[1] for details.\n> ++\n> +If no `-c` option is given, the name of the new branch will be derived\n> +from the remote-tracking branch, by looking at the local part of the\n> +refspec configured for the corresponding remote, and then stripping\n> +the initial part up to the \"*\".\n> +This would tell us to use \"hack\" as the local branch when branching\n> +off of \"origin/hack\" (or \"remotes/origin/hack\", or even\n> +\"refs/remotes/origin/hack\").  If the given name has no slash, or the above\n> +guessing results in an empty name, the guessing is aborted.  You can\n> +explicitly give a name with `-c` in such a case.\n> +\n> +--no-track::\n> +       Do not set up \"upstream\" configuration, even if the\n> +       branch.autoSetupMerge configuration variable is true.\n\nThese two options and the intervening paragraph, while they make sense\nto me, seem like the kind of stuff we'd want to throw out -- or at\nleast rework -- when trying to introduce a new command to simplify.\nBut I already discussed that above.\n\n> +-l::\n> +       Create the new branch's reflog; see linkgit:git-branch[1] for\n> +       details.\n\n??  Jettison this.\n\n> +\n> +--detach::\n> +       Rather than checking out a branch to work on it, check out a\n> +       commit for inspection and discardable experiments.\n> +       This is the default behavior of \"git checkout <commit>\" when\n> +       <commit> is not a branch name.  See the \"DETACHED HEAD\" section\n> +       below for details.\n> +\n> +--orphan <new_branch>::\n> +       Create a new 'orphan' branch, named <new_branch>, started from\n> +       <start_point> and switch to it.  The first commit made on this\n\nWhat??  started from <start_point>?  The whole point of --orphan is\nyou have no parent, i.e. no start point.  Also, why does the\nexplanation reference an argument that wasn't in the immediately\npreceding synopsis?\n\n> +       new branch will have no parents and it will be the root of a new\n> +       history totally disconnected from all the other branches and\n> +       commits.\n> ++\n> +The index and the working tree are adjusted as if you had previously run\n> +\"git checkout <start_point>\".  This allows you to start a new history\n> +that records a set of paths similar to <start_point> by easily running\n> +\"git commit -a\" to make the root commit.\n> ++\n> +This can be useful when you want to publish the tree from a commit\n> +without exposing its full history. You might want to do this to publish\n> +an open source branch of a project whose current tree is \"clean\", but\n> +whose full history contains proprietary or otherwise encumbered bits of\n> +code.\n> ++\n> +If you want to start a disconnected history that records a set of paths\n> +that is totally different from the one of <start_point>, then you should\n> +clear the index and the working tree right after creating the orphan\n> +branch by running \"git rm -rf .\" from the top level of the working tree.\n> +Afterwards you will be ready to prepare your new files, repopulating the\n> +working tree, by copying them from elsewhere, extracting a tarball, etc.\n\nIck.  Seems overly complex.  I'd rather that --orphan defaulted to\nclearing the index and working tree, and that one would need to pass\nHEAD for <start_point> if you wanted to start out with all those other\nfiles.  That would certainly make the explanation a little clearer to\nusers, and more natural when they start experimenting with it.\n\nHowever, --orphan is pretty special case.  Do we perhaps want to leave\nit out of this new command and only include it in checkout?\n\n> +-m::\n> +--merge::\n> +       If you have local modifications to one or more files that are\n> +       different between the current branch and the branch to which\n> +       you are switching, the command refuses to switch branches in\n> +       order to preserve your modifications in context.  However,\n> +       with this option, a three-way merge between the current\n> +       branch, your working tree contents, and the new branch is\n> +       done, and you will be on the new branch.\n> ++\n> +When a merge conflict happens, the index entries for conflicting\n> +paths are left unmerged, and you need to resolve the conflicts\n> +and mark the resolved paths with `git add` (or `git rm` if the merge\n> +should result in deletion of the path).\n> +\n> +--conflict=<style>::\n> +       The same as --merge option above, but changes the way the\n> +       conflicting hunks are presented, overriding the\n> +       merge.conflictStyle configuration variable.  Possible values are\n> +       \"merge\" (default) and \"diff3\" (in addition to what is shown by\n> +       \"merge\" style, shows the original contents).\n> +\n> +--ignore-other-worktrees::\n> +       `git switch-branch` refuses when the wanted ref is already\n> +       checked out by another worktree. This option makes it check\n> +       the ref out anyway. In other words, the ref can be held by\n> +       more than one worktree.\n\nseems rather dangerous...is the goal to be an easier-to-use suggestion\nfor new users while checkout continues to exist, or is this command\nmeant to handle all branch switching functionality that checkout has?\n\n> +\n> +--[no-]recurse-submodules::\n> +       Using --recurse-submodules will update the content of all initialized\n> +       submodules according to the commit recorded in the superproject. If\n> +       local modifications in a submodule would be overwritten the checkout\n> +       will fail unless `-f` is used. If nothing (or --no-recurse-submodules)\n> +       is used, the work trees of submodules will not be updated.\n> +       Just like linkgit:git-submodule[1], this will detach the\n> +       submodules HEAD.\n> +\n> +<branch>::\n> +       Branch to checkout; if it refers to a branch (i.e., a name that,\n> +       when prepended with \"refs/heads/\", is a valid ref), then that\n> +       branch is checked out. Otherwise, if it refers to a valid\n> +       commit, your HEAD becomes \"detached\" and you are no longer on\n> +       any branch (see below for details).\n\nI thought we requiring --detach in order to detach.  Does this\nparagraph need updating?  Also, if we require --detach when we'll be\ndetaching HEAD, then this paragraph gets a LOT simpler.\n\n> ++\n> +You can use the `\"@{-N}\"` syntax to refer to the N-th last\n> +branch/commit checked out using \"git checkout\" operation. You may\n> +also specify `-` which is synonymous to `\"@{-1}`.\n> ++\n> +As a special case, you may use `\"A...B\"` as a shortcut for the\n> +merge base of `A` and `B` if there is exactly one merge base. You can\n> +leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n\nI actually didn't know about the A...B special case for checkout.\nInteresting...but I'm starting to wonder if this is too much info for\na \"simplified command\".\n\n> +\n> +<new_branch>::\n> +       Name for the new branch.\n> +\n> +<start_point>::\n> +       The name of a commit at which to start the new branch; see\n> +       linkgit:git-branch[1] for details. Defaults to HEAD.\n\nErm, so if <start_point> is given, and there is an associated remote\ntracking branch for the given branch name, perhaps we don't set up the\nautomatic tracking in contrast to what I mentioned above?  Hmm...\n\n> +\n> +DETACHED HEAD\n> +-------------\n> +include::detach-head.txt[]\n> +\n> +EXAMPLES\n> +--------\n> +\n> +. The following sequence checks out the `master` branch.\n> ++\n> +------------\n> +$ git switch-branch master\n> +------------\n> ++\n> +\n> +. After working in the wrong branch, switching to the correct\n> +branch would be done using:\n> ++\n> +------------\n> +$ git switch-branch mytopic\n> +------------\n> ++\n> +However, your \"wrong\" branch and correct \"mytopic\" branch may\n> +differ in files that you have modified locally, in which case\n> +the above checkout would fail like this:\n> ++\n> +------------\n> +$ git switch-branch mytopic\n> +error: You have local changes to 'frotz'; not switching branches.\n> +------------\n> ++\n> +You can give the `-m` flag to the command, which would try a\n> +three-way merge:\n> ++\n> +------------\n> +$ git switch-branch -m mytopic\n> +Auto-merging frotz\n> +------------\n> ++\n> +After this three-way merge, the local modifications are _not_\n> +registered in your index file, so `git diff` would show you what\n> +changes you made since the tip of the new branch.\n> +\n> +. When a merge conflict happens during switching branches with\n> +the `-m` option, you would see something like this:\n> ++\n> +------------\n> +$ git switch-branch -m mytopic\n> +Auto-merging frotz\n> +ERROR: Merge conflict in frotz\n> +fatal: merge program failed\n> +------------\n> ++\n> +At this point, `git diff` shows the changes cleanly merged as in\n> +the previous example, as well as the changes in the conflicted\n> +files.  Edit and resolve the conflict and mark it resolved with\n> +`git add` as usual:\n> ++\n> +------------\n> +$ edit frotz\n> +$ git add frotz\n> +------------\n> +\n> +SEE ALSO\n> +--------\n> +linkgit:git-checkout[1]\n> +\n> +GIT\n> +---\n> +Part of the linkgit:git[1] suite\n> diff --git a/Makefile b/Makefile\n> index 1a44c811aa..f035dbab9e 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -777,9 +777,11 @@ BUILT_INS += git-format-patch$X\n>  BUILT_INS += git-fsck-objects$X\n>  BUILT_INS += git-init$X\n>  BUILT_INS += git-merge-subtree$X\n> +BUILT_INS += git-restore-files$X\n>  BUILT_INS += git-show$X\n>  BUILT_INS += git-stage$X\n>  BUILT_INS += git-status$X\n> +BUILT_INS += git-switch-branch$X\n>  BUILT_INS += git-whatchanged$X\n>\n>  # what 'all' will build and 'install' will install in gitexecdir,\n> diff --git a/builtin.h b/builtin.h\n> index 6538932e99..01ed43ea69 100644\n> --- a/builtin.h\n> +++ b/builtin.h\n> @@ -214,6 +214,7 @@ extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);\n>  extern int cmd_repack(int argc, const char **argv, const char *prefix);\n>  extern int cmd_rerere(int argc, const char **argv, const char *prefix);\n>  extern int cmd_reset(int argc, const char **argv, const char *prefix);\n> +extern int cmd_restore_files(int argc, const char **argv, const char *prefix);\n>  extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\n>  extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);\n>  extern int cmd_revert(int argc, const char **argv, const char *prefix);\n> @@ -227,6 +228,7 @@ extern int cmd_show_index(int argc, const char **argv, const char *prefix);\n>  extern int cmd_status(int argc, const char **argv, const char *prefix);\n>  extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n>  extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);\n> +extern int cmd_switch_branch(int argc, const char **argv, const char *prefix);\n>  extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n>  extern int cmd_tag(int argc, const char **argv, const char *prefix);\n>  extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 764e1a83a1..7dc0f4d3f3 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -33,6 +33,16 @@ static const char * const checkout_usage[] = {\n>         NULL,\n>  };\n>\n> +static const char * const switch_branch_usage[] = {\n> +       N_(\"git switch-branch [<options>] [<branch>]\"),\n> +       NULL,\n> +};\n> +\n> +static const char * const restore_files_usage[] = {\n> +       N_(\"git restore-files [<options>] [<branch>] -- <file>...\"),\n> +       NULL,\n> +};\n> +\n>  struct checkout_opts {\n>         int patch_mode;\n>         int quiet;\n> @@ -1302,31 +1312,23 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n>         return newopts;\n>  }\n>\n> -int cmd_checkout(int argc, const char **argv, const char *prefix)\n> +static int checkout_main(int argc, const char **argv, const char *prefix,\n> +                        struct checkout_opts *opts, struct option *options,\n> +                        const char * const usagestr[])\n>  {\n> -       struct checkout_opts real_opts;\n> -       struct checkout_opts *opts = &real_opts;\n>         struct branch_info new_branch_info;\n>         int dwim_remotes_matched = 0;\n> -       struct option *options = NULL;\n>\n> -       memset(opts, 0, sizeof(*opts));\n>         memset(&new_branch_info, 0, sizeof(new_branch_info));\n>         opts->overwrite_ignore = 1;\n>         opts->prefix = prefix;\n>         opts->show_progress = -1;\n> -       opts->dwim_new_local_branch = 1;\n>\n>         git_config(git_checkout_config, opts);\n>\n>         opts->track = BRANCH_TRACK_UNSPECIFIED;\n>\n> -       options = parse_options_dup(options);\n> -       options = add_common_options(opts, options);\n> -       options = add_switch_branch_options(opts, options);\n> -       options = add_checkout_path_options(opts, options);\n> -\n> -       argc = parse_options(argc, argv, prefix, options, checkout_usage,\n> +       argc = parse_options(argc, argv, prefix, options, usagestr,\n>                              PARSE_OPT_KEEP_DASHDASH);\n>\n>         if (opts->show_progress < 0) {\n> @@ -1455,3 +1457,61 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>                 return checkout_branch(opts, &new_branch_info);\n>         }\n>  }\n> +\n> +int cmd_checkout(int argc, const char **argv, const char *prefix)\n> +{\n> +       struct checkout_opts opts;\n> +       struct option *options = NULL;\n> +       int ret;\n> +\n> +       memset(&opts, 0, sizeof(opts));\n> +       opts.dwim_new_local_branch = 1;\n> +\n> +       options = parse_options_dup(options);\n> +       options = add_common_options(&opts, options);\n> +       options = add_switch_branch_options(&opts, options);\n> +       options = add_checkout_path_options(&opts, options);\n> +\n> +       ret = checkout_main(argc, argv, prefix, &opts,\n> +                           options, checkout_usage);\n> +       FREE_AND_NULL(options);\n> +       return ret;\n> +}\n> +\n> +int cmd_switch_branch(int argc, const char **argv, const char *prefix)\n> +{\n> +       struct checkout_opts opts;\n> +       struct option *options = NULL;\n> +       int ret;\n> +\n> +       memset(&opts, 0, sizeof(opts));\n> +       opts.dwim_new_local_branch = 1;\n> +\n> +       options = parse_options_dup(options);\n> +       options = add_common_options(&opts, options);\n> +       options = add_switch_branch_options(&opts, options);\n> +\n> +       ret = checkout_main(argc, argv, prefix, &opts,\n> +                           options, switch_branch_usage);\n> +       FREE_AND_NULL(options);\n> +       return ret;\n> +}\n> +\n> +int cmd_restore_files(int argc, const char **argv, const char *prefix)\n> +{\n> +       struct checkout_opts opts;\n> +       struct option *options = NULL;\n> +       int ret;\n> +\n> +       memset(&opts, 0, sizeof(opts));\n> +       opts.dwim_new_local_branch = 1;\n> +\n> +       options = parse_options_dup(options);\n> +       options = add_common_options(&opts, options);\n> +       options = add_checkout_path_options(&opts, options);\n> +\n> +       ret = checkout_main(argc, argv, prefix, &opts,\n> +                           options, restore_files_usage);\n> +       FREE_AND_NULL(options);\n> +       return ret;\n> +}\n> diff --git a/command-list.txt b/command-list.txt\n> index 3a9af104b5..4638802754 100644\n> --- a/command-list.txt\n> +++ b/command-list.txt\n> @@ -151,6 +151,7 @@ git-replace                             ancillarymanipulators           complete\n>  git-request-pull                        foreignscminterface             complete\n>  git-rerere                              ancillaryinterrogators\n>  git-reset                               mainporcelain           worktree\n> +git-restore-files                       mainporcelain           worktree\n>  git-revert                              mainporcelain\n>  git-rev-list                            plumbinginterrogators\n>  git-rev-parse                           plumbinginterrogators\n> @@ -171,6 +172,7 @@ git-status                              mainporcelain           info\n>  git-stripspace                          purehelpers\n>  git-submodule                           mainporcelain\n>  git-svn                                 foreignscminterface\n> +git-switch-branch                       mainporcelain           history\n>  git-symbolic-ref                        plumbingmanipulators\n>  git-tag                                 mainporcelain           history\n>  git-unpack-file                         plumbinginterrogators\n> diff --git a/git.c b/git.c\n> index 2f604a41ea..a2be6c3eb5 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -542,6 +542,7 @@ static struct cmd_struct commands[] = {\n>         { \"replace\", cmd_replace, RUN_SETUP },\n>         { \"rerere\", cmd_rerere, RUN_SETUP },\n>         { \"reset\", cmd_reset, RUN_SETUP },\n> +       { \"restore-files\", cmd_restore_files, RUN_SETUP | NEED_WORK_TREE },\n>         { \"rev-list\", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },\n>         { \"rev-parse\", cmd_rev_parse, NO_PARSEOPT },\n>         { \"revert\", cmd_revert, RUN_SETUP | NEED_WORK_TREE },\n> @@ -557,6 +558,7 @@ static struct cmd_struct commands[] = {\n>         { \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n>         { \"stripspace\", cmd_stripspace },\n>         { \"submodule--helper\", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },\n> +       { \"switch-branch\", cmd_switch_branch, RUN_SETUP | NEED_WORK_TREE },\n>         { \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n>         { \"tag\", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },\n>         { \"unpack-file\", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },\n> --\n> 2.20.0.rc1.380.g3eb999425c.dirty\n>\n"},{"id":"364515","messageId":"CABPp-BGsw3cxU4Y+-UMcwk=skyuvgU_Rfkyh0o1rRPwOv_LDDA@mail.gmail.com","threadId":"49793","inReplyTo":"20181129215850.7278-1-pclouds@gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2018-12-04T01:28:53Z","receivedAt":"2018-12-04T01:29:09Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Nov 29, 2018 at 2:01 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> v3 sees switch-branch go back to switch-branch (in v2 it was\n> checkout-branch). checkout-files is also renamed restore-files (v1 was\n> restore-paths). Hopefully we won't see another rename.\n\nI started reading through the patches.  I also tried to apply them\nlocally, but they had conflicts or missing base file version on both\nmaster and next.  What version did you base it on?\n\nI stopped at 07/14, and dropped my comments all there.  I didn't read\nany further yet, and may wait for your post-2.20 reroll.\n\n> I'll try to summarize the differences between the new commands and\n> 'git checkout' down here, but you're welcome to just head to 07/14 and\n> read the new man pages.\n>\n> 'git switch-branch'\n>\n> - does not \"do nothing\", you have to either switch branch, create a\n>   new branch, or detach. \"git switch-branch\" with no arguments is\n>   rejected.\n>\n> - implicit detaching is rejected. If you need to detach, you need to\n>   give --detach. Or stick to 'git checkout'.\n>\n> - -b/-B is renamed to -c/-C with long option names\n>\n> - of course does not accept pathspec\n>\n> 'git restore-files'\n>\n> - takes a ref from --from argument, not as a free ref. As a result,\n>   '--' is no longer needed. All non-option arguments are pathspec\n>\n> - pathspec is mandatory, you can't do \"git restore-files\" without any\n>   pathspec.\n>\n> - I just remember -p which is allowed to take no pathspec :( I'll fix\n>   it later.\n\nThis all looks good.  I commented elsewhere but please remember that\npathspec implies directories as a possibility and we really need to\nfix the broken behavior of checkout when given a directory.\n\n> - Two more fancy features (the \"git checkout --index\" being the\n>   default mode and the backup log for accidental overwrites) are of\n>   course still missing. But they are coming.\n>\n> I did not go replace \"detached HEAD\" with \"unnamed branch\" (or \"no\n> branch\") everywhere because I think a unique term is still good to\n> refer to this concept. Or maybe \"no branch\" is good enough. I dunno.\n\nI personally like \"unnamed branch\", but \"no branch\" would still be\nbetter than \"detached HEAD\".\n"},{"id":"364525","messageId":"xmqq1s6yezk3.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CABPp-BHQ68pkvO8yXYuy=0D6ne8u=5CUMDqiN0jtRrxCL55n2g@mail.gmail.com","subject":"Re: [PATCH v3 07/14] checkout: split into switch-branch and restore-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-12-04T03:33:16Z","receivedAt":"2018-12-04T03:33:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n>> +Updates files in the working tree to match the version in the index\n>> +or the specified tree.\n>> +\n>> +'git restore-files' [--from=<tree-ish>] <pathspec>...::\n>\n> <tree-ish> and <pathspec>?  I understand <commit-ish> and <pathspec>,\n> or <tree-ish> but have no clue why it'd be okay to specify <tree-ish>\n> and <pathspec> together.  What does that even mean?\n\nI have this tree object v2.6.11-tree that is not enclosed in a\ncommit object.  I want to take the top-level Makefile out of that\ntree, stuff it in the index and overwrite the working tree file.\n\n\t$ git checkout v2.6.11-tree Makefile\n\t$ git restore-files --from=v2.6.11-tree Makefile\n\n>> +       Overwrite paths in the working tree by replacing with the\n>> +       contents in the index or in the <tree-ish> (most often a\n>> +       commit).  When a <tree-ish> is given, the paths that\n>> +       match the <pathspec> are updated both in the index and in\n>> +       the working tree.\n>\n> Is that the default we really want for this command?  Why do we\n> automatically assume these files are ready for commit?  I understand\n> that it's what checkout did, but I'd find it more natural to only\n> affect the working tree by default.  We can give it an option for\n> affecting the index instead (or perhaps in addition).\n\nOooah.  Now this is getting juicy.  \n\nI do think supporting \"--index\" (which would make it more in line\nwith what Duy wrote), with optionally \"--cached\" as well, and making\nthe \"working tree only\" mode as default may not be a bad idea.  I am\noffhand not sure how the \"working tree only\" mode (similar to the\ndefault mode of \"git apply\" that mimics the way \"patch -p1\" works)\nshould interact with the non-overlay mode of the command, but other\nthan that, I tend to agree with the idea that restore-files is only\na part of making the contents into committable shape, not exactly\nready for it yet.\n"},{"id":"364558","messageId":"CACsJy8BTs+WKzTTEF2XVTT-LVJk_exYCz_hN+hXU1Dw+oquBpA@mail.gmail.com","threadId":"49793","inReplyTo":"CABPp-BHQ68pkvO8yXYuy=0D6ne8u=5CUMDqiN0jtRrxCL55n2g@mail.gmail.com","subject":"Re: [PATCH v3 07/14] checkout: split into switch-branch and restore-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-12-04T16:21:37Z","receivedAt":"2018-12-04T16:22:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Thanks for all the comments! There are still some I haven't replied\n(either I'll agree and do it anyway, or I'll need some more time to\ndigest)\n\nOn Tue, Dec 4, 2018 at 1:45 AM Elijah Newren <newren@gmail.com> wrote:\n> > +'git restore-files' [--from=<tree-ish>] <pathspec>...\n>\n> Isn't this already inferred by the previous line?  Or was the\n> inclusion of --from on the previous line in error?  Looking at the\n> git-checkout manpage, it looks like you may have just been copying an\n> existing weirdness, but it needs to be fixed.  ;-)\n\nHehe.\n\n> > +'git restore-files' (-p|--patch) [--from=<tree-ish>] [<pathspec>...]\n> > +\n> > +DESCRIPTION\n> > +-----------\n> > +Updates files in the working tree to match the version in the index\n> > +or the specified tree.\n> > +\n> > +'git restore-files' [--from=<tree-ish>] <pathspec>...::\n>\n> <tree-ish> and <pathspec>?  I understand <commit-ish> and <pathspec>,\n> or <tree-ish> but have no clue why it'd be okay to specify <tree-ish>\n> and <pathspec> together.  What does that even mean?\n>\n> Also, rather than fixing from <tree-ish> to <commit-ish> or <commit>,\n> perhaps we should just use <revision> here?  (I'm thinking of git\n> rev-parse's \"Specifying revisions\", which suggests \"revisions\" as a\n> good substitute for \"commit-ish\" that isn't quite so weird for new\n> users.)\n\ntree-ish is technically more accurate. But I'm ok with just\n<revision>. If you give it a blob oid then you should get a nice\nexplanation what you're doing wrong anyway.\n\n\n> > +       Overwrite paths in the working tree by replacing with the\n> > +       contents in the index or in the <tree-ish> (most often a\n> > +       commit).  When a <tree-ish> is given, the paths that\n> > +       match the <pathspec> are updated both in the index and in\n> > +       the working tree.\n>\n> Is that the default we really want for this command?  Why do we\n> automatically assume these files are ready for commit?  I understand\n> that it's what checkout did, but I'd find it more natural to only\n> affect the working tree by default.  We can give it an option for\n> affecting the index instead (or perhaps in addition).\n\nYeah, that behavior of updating the index always bothers me when I use\nit but I seemed to forget when working on this.\n\n> > +--ours::\n> > +--theirs::\n> > +       Check out stage #2 ('ours') or #3 ('theirs') for unmerged\n> > +       paths.\n> > ++\n> > +Note that during `git rebase` and `git pull --rebase`, 'ours' and\n> > +'theirs' may appear swapped; `--ours` gives the version from the\n> > +branch the changes are rebased onto, while `--theirs` gives the\n> > +version from the branch that holds your work that is being rebased.\n> > ++\n> > +This is because `rebase` is used in a workflow that treats the\n> > +history at the remote as the shared canonical one, and treats the\n> > +work done on the branch you are rebasing as the third-party work to\n> > +be integrated, and you are temporarily assuming the role of the\n> > +keeper of the canonical history during the rebase.  As the keeper of\n> > +the canonical history, you need to view the history from the remote\n> > +as `ours` (i.e. \"our shared canonical history\"), while what you did\n> > +on your side branch as `theirs` (i.e. \"one contributor's work on top\n> > +of it\").\n>\n> Total aside because I'm not sure what you could change here, but man\n> do I hate this.\n\nUh it's actually documented? I'm always confused by this too. --ours\nand --theirs at this point are pretty much tied to stage 2 and 3.\nNothing I can do about it. But if you could come up with some other\noption names, then we could make \"new ours\" to be stage 3 during\nrebase, for example.\n\n> > +Part of the linkgit:git[1] suite\n>\n>\n> My single biggest worry about this whole series is that I'm worried\n> you're perpetuating and even further ingraining one of the biggest\n> usability problems with checkout: people suggest and use it for\n> reverting/restoring paths to a previous version, but it doesn't do\n> that:\n>\n> git restore-files --from master~10 Documentation/\n> <edit some non-documentation files>\n> git add -u\n> git commit -m \"Rationale for changing files including reverting Documentation/\"\n>\n> In particular, now you have a mixture of files in Documentation/ from\n> master~10 (er, now likely master~11) and HEAD~1; any files and\n> sub-directories that existed in HEAD~1 still remain and are mixed with\n> all other files in Documentation/ from the older commit.\n>\n> You may think this is a special case, but this particular issue\n> results in some pretty bad surprises.  Also, it was pretty surprising\n> to me just how difficult it was to implement an svn-like revert in\n> EasyGit, in large part because of this 'oversight' in git.  git\n> checkout -- <paths> to me has always been fundamentally wrong, but I\n> just wasn't sure if I wanted to fight the backward compatibility\n> battle and suggest changing it.  With a new command, we definitely\n> shouldn't be reinforcing this error.  (And maybe we should consider\n> taking the time to fix git checkout too.)\n\nWhat would be the right behavior for\n\n git restore-files --from=master~10 Documentation/\n\nthen? Consider it an error? I often use \"git checkout HEAD\" and \"git\ncheckout HEAD^\" (usually with -p) but not very far back like\nmaster~10.\n\n> > +If the branch exists in multiple remotes and one of them is named by\n> > +the `checkout.defaultRemote` configuration variable, we'll use that\n> > +one for the purposes of disambiguation, even if the `<branch>` isn't\n> > +unique across all remotes. Set it to\n> > +e.g. `checkout.defaultRemote=origin` to always checkout remote\n> > +branches from there if `<branch>` is ambiguous but exists on the\n> > +'origin' remote. See also `checkout.defaultRemote` in\n> > +linkgit:git-config[1].\n>\n> So switch-branch will be controlled by checkout.* config variables?\n> That probably makes the most sense, but it does dilute the advantage\n> of adding these simpler commands.\n>\n> Also, the fact that we're trying to make a simpler command makes me\n> think that removing the auto-vivify behavior from the default and\n> adding a simple flag which users can pass to request will allow this\n> part of the documentation to be hidden behind the appropriate flag,\n> which may make it easier for users to compartmentalize the command and\n> it's options, enabling them to learn as they go.\n\nSounds good. I don't know a good name for this new option though so\nunless anybody comes up with some suggestion, I'll just disable\ncheckout.defaultRemote in switch-branch. If it comes back as a new\noption, it can always be added later.\n\n> > +'git switch-branch' -c|-C <new_branch> [<start_point>]::\n> > +\n> > +       Specifying `-c` causes a new branch to be created as if\n> > +       linkgit:git-branch[1] were called and then switched to. In\n> > +       this case you can use the `--track` or `--no-track` options,\n> > +       which will be passed to 'git branch'.  As a convenience,\n> > +       `--track` without `-c` implies branch creation; see the\n> > +       description of `--track` below.\n>\n> Can we get rid of --track/--no-track and just provide a flag (which\n> takes no arguments) for the user to use?  Problems with --track:\n>   * it's not even in your synopsis\n>   * user has to repeat themselves (e.g. 'next' in two places from '-c\n> next --track origin/next'); this repetition is BOTH laborious AND\n> error-prone\n>   * it's rather inconsistent: --track is the default due to\n> auto-vivify when the user specifies nothing but a branch name that\n> doesn't exist yet, but when the user realizes the branch doesn't exist\n> yet and asks to have it created then suddenly tracking is not the\n> default??\n\nI don't think --track is default anymore (maybe I haven't updated the\nman page correctly). The dwim behavior is only activated in\nswitch-branch when you specify --guess to reduce the amount of magic\nwe throw at the user. With that in mind, do we still hide\n--track/--no-track from switch-branch?\n\n> I'm not sure what's best, but here's some food for thought:\n>\n>\n>    git switch-branch <branch>\n> switches to <branch>, if it exists.  Error cases:\n>   * If <branch> isn't actually a branch but a <tag> or\n> <remote-tracking-branch> or <revision>, error out and suggest using\n> --detach.\n>   * If <branch> isn't actually a branch but there is a similarly named\n> <remote-tracking-branch> (e.g. origin/<branch>), then suggest using\n> -c.\n\nI would make these advice so I can hide them. Or if I manage to make\nall these hints one line then I'll make it unconditional.\n\n>   git switch-branch -c <branch>\n> creates <branch> and, if a relevant-remote-tracking branch exists,\n> base the branch on that revision and set the new branch up to track\n\nHmm.. this is a bit magical and could be surprising. If I create (and\nswitch to) a new branch foo, I don't necessarily mean tracking\norigin/foo (I may not even think about origin/foo when I type the\ncommand). So tentatively no.\n\n> > +If `-C` is given, <new_branch> is created if it doesn't exist;\n> > +otherwise, it is reset. This is the transactional equivalent of\n> > ++\n> > +------------\n> > +$ git branch -f <branch> [<start_point>]\n> > +$ git switch-branch <branch>\n> > +------------\n> > ++\n> > +that is to say, the branch is not reset/created unless \"git\n> > +switch-branch\" is successful.\n>\n> ...and when exactly would it fail?  Reading this, it looks like the\n> only possible error condition was removed due saying we'll reset the\n> branch if it already exists, so it's rather confusing.\n\nYeah probably just scrape it. The atomic nature is not worth highlighting.\n\n\n> > +'git switch-branch' --detach [<commit>]::\n> > +\n> > +       Prepare to work on a unnamed branch on top of <commit> (see\n> > +       \"DETACHED HEAD\" section), and updating the index and the files\n> > +       in the working tree.  Local modifications to the files in the\n> > +       working tree are kept, so that the resulting working tree will\n> > +       be the state recorded in the commit plus the local\n> > +       modifications.\n> > ++\n> > +When the <commit> argument is a branch name, the `--detach` option can\n> > +be used to detach HEAD at the tip of the branch (`git switch-branch\n> > +<branch>` would check out that branch without detaching HEAD).\n> > ++\n> > +Omitting <commit> detaches HEAD at the tip of the current branch.\n> > +\n> > +OPTIONS\n> > +-------\n> > +-q::\n> > +--quiet::\n> > +       Quiet, suppress feedback messages.\n> > +\n> > +--[no-]progress::\n> > +       Progress status is reported on the standard error stream\n> > +       by default when it is attached to a terminal, unless `--quiet`\n> > +       is specified. This flag enables progress reporting even if not\n> > +       attached to a terminal, regardless of `--quiet`.\n> > +\n> > +-f::\n> > +--force::\n> > +       Proceed even if the index or the working tree differs from\n> > +       HEAD.  This is used to throw away local changes.\n>\n> Haven't thought through this thoroughly, but do we really need an\n> option for that instead of telling users to 'git reset --hard HEAD'\n> before switching branches if they want their stuff thrown away?\n\nFor me it's just a bit more convenient. Hit an error when switching\nbranch? Recall the command from bash history, stick -f in it and run.\nElsewhere I think both Junio and Thomas (or maybe only Junio) suggests\nmoving the \"git reset\" functionality without moving HEAD to one of\nthese commands, which goes the opposite direction...\n\n> > +-c <new_branch>::\n> > +--create <new_branch>::\n> > +       Create a new branch named <new_branch> and start it at\n> > +       <start_point>; see linkgit:git-branch[1] for details.\n> > +\n> > +-C <new_branch>::\n> > +--force-create <new_branch>::\n> > +       Creates the branch <new_branch> and start it at <start_point>;\n> > +       if it already exists, then reset it to <start_point>. This is\n> > +       equivalent to running \"git branch\" with \"-f\"; see\n> > +       linkgit:git-branch[1] for details.\n>\n> Makes sense, but let's get the -b/-B vs. -c/-C consistent.\n\nAnother option I'm considering is -n/-N (for _new_ branch). Maybe\n-c/-C is good enough.\n\n> > +-l::\n> > +       Create the new branch's reflog; see linkgit:git-branch[1] for\n> > +       details.\n>\n> ??  Jettison this.\n\nYep. It looks weird to me too. reflog is just behind the scene these\ndays. Nobody need to explicitly ask for reflog anymore.\n\n> > +--orphan <new_branch>::\n> > +       Create a new 'orphan' branch, named <new_branch>, started from\n> > +       <start_point> and switch to it.  The first commit made on this\n>\n> What??  started from <start_point>?  The whole point of --orphan is\n> you have no parent, i.e. no start point.  Also, why does the\n> explanation reference an argument that wasn't in the immediately\n> preceding synopsis?\n\nI guess bad phrasing. It should be \"switch to <start_point> first,\nthen prepare the worktree so that the first commit will have no\nparent\". Or something along that line.\n\nYou should really review git-checkout.txt btw ;-)\n\n> > +       new branch will have no parents and it will be the root of a new\n> > +       history totally disconnected from all the other branches and\n> > +       commits.\n> > ++\n> > +The index and the working tree are adjusted as if you had previously run\n> > +\"git checkout <start_point>\".  This allows you to start a new history\n> > +that records a set of paths similar to <start_point> by easily running\n> > +\"git commit -a\" to make the root commit.\n> > ++\n> > +This can be useful when you want to publish the tree from a commit\n> > +without exposing its full history. You might want to do this to publish\n> > +an open source branch of a project whose current tree is \"clean\", but\n> > +whose full history contains proprietary or otherwise encumbered bits of\n> > +code.\n> > ++\n> > +If you want to start a disconnected history that records a set of paths\n> > +that is totally different from the one of <start_point>, then you should\n> > +clear the index and the working tree right after creating the orphan\n> > +branch by running \"git rm -rf .\" from the top level of the working tree.\n> > +Afterwards you will be ready to prepare your new files, repopulating the\n> > +working tree, by copying them from elsewhere, extracting a tarball, etc.\n>\n> Ick.  Seems overly complex.  I'd rather that --orphan defaulted to\n> clearing the index and working tree, and that one would need to pass\n> HEAD for <start_point> if you wanted to start out with all those other\n> files.  That would certainly make the explanation a little clearer to\n> users, and more natural when they start experimenting with it.\n>\n> However, --orphan is pretty special case.  Do we perhaps want to leave\n> it out of this new command and only include it in checkout?\n\nI started this by simply splitting git-checkout in two commands that,\ncombined, can do everything git-checkout can. Then suggestions to have\nbetter default came in and I think we started to drift further to\n_removing_ options and falling back to git-checkout.\n\nI think we could still keep \"complicated\" options as long as they are\nclearly described and don't surprise users until they figure them out.\nThat way I don't have to go back to git-checkout and deal with all the\nambiguation it creates.\n\n> > +-m::\n> > +--merge::\n> > +       If you have local modifications to one or more files that are\n> > +       different between the current branch and the branch to which\n> > +       you are switching, the command refuses to switch branches in\n> > +       order to preserve your modifications in context.  However,\n> > +       with this option, a three-way merge between the current\n> > +       branch, your working tree contents, and the new branch is\n> > +       done, and you will be on the new branch.\n> > ++\n> > +When a merge conflict happens, the index entries for conflicting\n> > +paths are left unmerged, and you need to resolve the conflicts\n> > +and mark the resolved paths with `git add` (or `git rm` if the merge\n> > +should result in deletion of the path).\n> > +\n> > +--conflict=<style>::\n> > +       The same as --merge option above, but changes the way the\n> > +       conflicting hunks are presented, overriding the\n> > +       merge.conflictStyle configuration variable.  Possible values are\n> > +       \"merge\" (default) and \"diff3\" (in addition to what is shown by\n> > +       \"merge\" style, shows the original contents).\n> > +\n> > +--ignore-other-worktrees::\n> > +       `git switch-branch` refuses when the wanted ref is already\n> > +       checked out by another worktree. This option makes it check\n> > +       the ref out anyway. In other words, the ref can be held by\n> > +       more than one worktree.\n>\n> seems rather dangerous...is the goal to be an easier-to-use suggestion\n> for new users while checkout continues to exist, or is this command\n> meant to handle all branch switching functionality that checkout has?\n\nAs explained above. I'm still thinking the latter, but with fewer\nsurprises and confusion. Though I guess I could be convinced to go\nwith the former (the problem with the former is, even as a\nno-longer-new user, I still find git-checkout not that pleasant to use\nand want a better replacement)\n\n> > +<branch>::\n> > +       Branch to checkout; if it refers to a branch (i.e., a name that,\n> > +       when prepended with \"refs/heads/\", is a valid ref), then that\n> > +       branch is checked out. Otherwise, if it refers to a valid\n> > +       commit, your HEAD becomes \"detached\" and you are no longer on\n> > +       any branch (see below for details).\n>\n> I thought we requiring --detach in order to detach.  Does this\n> paragraph need updating?  Also, if we require --detach when we'll be\n> detaching HEAD, then this paragraph gets a LOT simpler.\n\nYep. I really need to read through the document and update all of it.\n\n> > +You can use the `\"@{-N}\"` syntax to refer to the N-th last\n> > +branch/commit checked out using \"git checkout\" operation. You may\n> > +also specify `-` which is synonymous to `\"@{-1}`.\n> > ++\n> > +As a special case, you may use `\"A...B\"` as a shortcut for the\n> > +merge base of `A` and `B` if there is exactly one merge base. You can\n> > +leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n>\n> I actually didn't know about the A...B special case for checkout.\n> Interesting...but I'm starting to wonder if this is too much info for\n> a \"simplified command\".\n\nI could just hint about A...B and send the user to git-checkout.txt if\nthey need to know more. They can learn about git-checkout that way\ntoo.\n-- \nDuy\n"},{"id":"364559","messageId":"CACsJy8DEMHFTnL2QJu5Csb1jUQeu0HiT3rTDii4krrEJcoh=Qw@mail.gmail.com","threadId":"49793","inReplyTo":"CABPp-BGsw3cxU4Y+-UMcwk=skyuvgU_Rfkyh0o1rRPwOv_LDDA@mail.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-12-04T16:27:55Z","receivedAt":"2018-12-04T16:28:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Dec 4, 2018 at 2:29 AM Elijah Newren <newren@gmail.com> wrote:\n>\n> On Thu, Nov 29, 2018 at 2:01 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> >\n> > v3 sees switch-branch go back to switch-branch (in v2 it was\n> > checkout-branch). checkout-files is also renamed restore-files (v1 was\n> > restore-paths). Hopefully we won't see another rename.\n>\n> I started reading through the patches.  I also tried to apply them\n> locally, but they had conflicts or missing base file version on both\n> master and next.  What version did you base it on?\n\nI think nd/checkout-dwim-fix because of a non-trivial conflict there\n(but I don't remember when I noticed it and rebased on that). Anyway\nyou can get the whole series at\n\nhttps://gitlab.com/pclouds/git/tree/switch-branch-and-checkout-files\n\nIt fixes some of your comments already, a couple of bug fixes here and\nthere and in a good-enough shape that I start actually using it.\n\n> > - Two more fancy features (the \"git checkout --index\" being the\n> >   default mode and the backup log for accidental overwrites) are of\n> >   course still missing. But they are coming.\n> >\n> > I did not go replace \"detached HEAD\" with \"unnamed branch\" (or \"no\n> > branch\") everywhere because I think a unique term is still good to\n> > refer to this concept. Or maybe \"no branch\" is good enough. I dunno.\n>\n> I personally like \"unnamed branch\", but \"no branch\" would still be\n> better than \"detached HEAD\".\n\nHaven't really worked on killing the term \"detached HEAD\" yet. But I\nnoticed the other day that git-branch reports\n\n* (HEAD detached from 703266f6e4)\n\nand I didn't know how to rephrase that. I guess \"unnamed branch from\n703266f6e4\" is probably good enough but my old-timer brain screams no.\n-- \nDuy\n"},{"id":"364566","messageId":"CABPp-BGRcaiiD-aks1kaLr7ATLQ_oGSyooQBDD+2acgerA+Phg@mail.gmail.com","threadId":"49793","inReplyTo":"CACsJy8BTs+WKzTTEF2XVTT-LVJk_exYCz_hN+hXU1Dw+oquBpA@mail.gmail.com","subject":"Re: [PATCH v3 07/14] checkout: split into switch-branch and restore-files","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2018-12-04T17:43:46Z","receivedAt":"2018-12-04T17:44:04Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Dec 4, 2018 at 8:22 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> Thanks for all the comments! There are still some I haven't replied\n> (either I'll agree and do it anyway, or I'll need some more time to\n> digest)\n>\n> On Tue, Dec 4, 2018 at 1:45 AM Elijah Newren <newren@gmail.com> wrote:\n> > > +'git restore-files' [--from=<tree-ish>] <pathspec>...\n> >\n> > Isn't this already inferred by the previous line?  Or was the\n> > inclusion of --from on the previous line in error?  Looking at the\n> > git-checkout manpage, it looks like you may have just been copying an\n> > existing weirdness, but it needs to be fixed.  ;-)\n>\n> Hehe.\n>\n> > > +'git restore-files' (-p|--patch) [--from=<tree-ish>] [<pathspec>...]\n> > > +\n> > > +DESCRIPTION\n> > > +-----------\n> > > +Updates files in the working tree to match the version in the index\n> > > +or the specified tree.\n> > > +\n> > > +'git restore-files' [--from=<tree-ish>] <pathspec>...::\n> >\n> > <tree-ish> and <pathspec>?  I understand <commit-ish> and <pathspec>,\n> > or <tree-ish> but have no clue why it'd be okay to specify <tree-ish>\n> > and <pathspec> together.  What does that even mean?\n> >\n> > Also, rather than fixing from <tree-ish> to <commit-ish> or <commit>,\n> > perhaps we should just use <revision> here?  (I'm thinking of git\n> > rev-parse's \"Specifying revisions\", which suggests \"revisions\" as a\n> > good substitute for \"commit-ish\" that isn't quite so weird for new\n> > users.)\n>\n> tree-ish is technically more accurate. But I'm ok with just\n> <revision>. If you give it a blob oid then you should get a nice\n> explanation what you're doing wrong anyway.\n\nDocumenting as <revision> but having it be more general under the hood\nand actually accept <tree-ish> sounds good to me.  I just think the\npain of trying to explain <tree-ish> is too much of a hurdle for\nusers, especially as I expect it to be very unlikely that users will\never take advantage of it.\n\n> > > +       Overwrite paths in the working tree by replacing with the\n> > > +       contents in the index or in the <tree-ish> (most often a\n> > > +       commit).  When a <tree-ish> is given, the paths that\n> > > +       match the <pathspec> are updated both in the index and in\n> > > +       the working tree.\n> >\n> > Is that the default we really want for this command?  Why do we\n> > automatically assume these files are ready for commit?  I understand\n> > that it's what checkout did, but I'd find it more natural to only\n> > affect the working tree by default.  We can give it an option for\n> > affecting the index instead (or perhaps in addition).\n>\n> Yeah, that behavior of updating the index always bothers me when I use\n> it but I seemed to forget when working on this.\n>\n> > > +--ours::\n> > > +--theirs::\n> > > +       Check out stage #2 ('ours') or #3 ('theirs') for unmerged\n> > > +       paths.\n> > > ++\n> > > +Note that during `git rebase` and `git pull --rebase`, 'ours' and\n> > > +'theirs' may appear swapped; `--ours` gives the version from the\n> > > +branch the changes are rebased onto, while `--theirs` gives the\n> > > +version from the branch that holds your work that is being rebased.\n> > > ++\n> > > +This is because `rebase` is used in a workflow that treats the\n> > > +history at the remote as the shared canonical one, and treats the\n> > > +work done on the branch you are rebasing as the third-party work to\n> > > +be integrated, and you are temporarily assuming the role of the\n> > > +keeper of the canonical history during the rebase.  As the keeper of\n> > > +the canonical history, you need to view the history from the remote\n> > > +as `ours` (i.e. \"our shared canonical history\"), while what you did\n> > > +on your side branch as `theirs` (i.e. \"one contributor's work on top\n> > > +of it\").\n> >\n> > Total aside because I'm not sure what you could change here, but man\n> > do I hate this.\n>\n> Uh it's actually documented? I'm always confused by this too. --ours\n> and --theirs at this point are pretty much tied to stage 2 and 3.\n> Nothing I can do about it. But if you could come up with some other\n> option names, then we could make \"new ours\" to be stage 3 during\n> rebase, for example.\n\nI don't think it's a naming issue, personally.  Years ago we could\nhave defined --ours and --theirs differently based on which kind of\noperation we were in the middle of, but you are probably right that\nthey are now tied to stage 2 and 3.  But there's another place that we\nmight still be able to address this; I think the brain-damage here may\nhave just been due to the fact that the recursive merge machinery was\nrather inflexible and required HEAD to be stage 2.  If it were a\nlittle more flexible, then we might just be able to make this problem\ngo away.  Maybe it can still be fixed (I haven't dug too deeply into\nit), but if so, the only fix needed here would be to remove this long\nexplanation about why the tool gets things totally backward.\n\n> > > +Part of the linkgit:git[1] suite\n> >\n> >\n> > My single biggest worry about this whole series is that I'm worried\n> > you're perpetuating and even further ingraining one of the biggest\n> > usability problems with checkout: people suggest and use it for\n> > reverting/restoring paths to a previous version, but it doesn't do\n> > that:\n> >\n> > git restore-files --from master~10 Documentation/\n> > <edit some non-documentation files>\n> > git add -u\n> > git commit -m \"Rationale for changing files including reverting Documentation/\"\n> >\n> > In particular, now you have a mixture of files in Documentation/ from\n> > master~10 (er, now likely master~11) and HEAD~1; any files and\n> > sub-directories that existed in HEAD~1 still remain and are mixed with\n> > all other files in Documentation/ from the older commit.\n> >\n> > You may think this is a special case, but this particular issue\n> > results in some pretty bad surprises.  Also, it was pretty surprising\n> > to me just how difficult it was to implement an svn-like revert in\n> > EasyGit, in large part because of this 'oversight' in git.  git\n> > checkout -- <paths> to me has always been fundamentally wrong, but I\n> > just wasn't sure if I wanted to fight the backward compatibility\n> > battle and suggest changing it.  With a new command, we definitely\n> > shouldn't be reinforcing this error.  (And maybe we should consider\n> > taking the time to fix git checkout too.)\n>\n> What would be the right behavior for\n>\n>  git restore-files --from=master~10 Documentation/\n>\n> then? Consider it an error? I often use \"git checkout HEAD\" and \"git\n> checkout HEAD^\" (usually with -p) but not very far back like\n> master~10.\n\nWell, when you use a file rather than a directory:\n  git restore-files --from=master~10 foo.c\nthen you expect\n  git diff master~10 foo.c\nto come back empty afterward.  I expect the same if I give a directory\nrather than a file.  (Even if it does make 'restore-files' feel\nslightly mis-named.)\n\n> > > +If the branch exists in multiple remotes and one of them is named by\n> > > +the `checkout.defaultRemote` configuration variable, we'll use that\n> > > +one for the purposes of disambiguation, even if the `<branch>` isn't\n> > > +unique across all remotes. Set it to\n> > > +e.g. `checkout.defaultRemote=origin` to always checkout remote\n> > > +branches from there if `<branch>` is ambiguous but exists on the\n> > > +'origin' remote. See also `checkout.defaultRemote` in\n> > > +linkgit:git-config[1].\n> >\n> > So switch-branch will be controlled by checkout.* config variables?\n> > That probably makes the most sense, but it does dilute the advantage\n> > of adding these simpler commands.\n> >\n> > Also, the fact that we're trying to make a simpler command makes me\n> > think that removing the auto-vivify behavior from the default and\n> > adding a simple flag which users can pass to request will allow this\n> > part of the documentation to be hidden behind the appropriate flag,\n> > which may make it easier for users to compartmentalize the command and\n> > it's options, enabling them to learn as they go.\n>\n> Sounds good. I don't know a good name for this new option though so\n> unless anybody comes up with some suggestion, I'll just disable\n> checkout.defaultRemote in switch-branch. If it comes back as a new\n> option, it can always be added later.\n>\n> > > +'git switch-branch' -c|-C <new_branch> [<start_point>]::\n> > > +\n> > > +       Specifying `-c` causes a new branch to be created as if\n> > > +       linkgit:git-branch[1] were called and then switched to. In\n> > > +       this case you can use the `--track` or `--no-track` options,\n> > > +       which will be passed to 'git branch'.  As a convenience,\n> > > +       `--track` without `-c` implies branch creation; see the\n> > > +       description of `--track` below.\n> >\n> > Can we get rid of --track/--no-track and just provide a flag (which\n> > takes no arguments) for the user to use?  Problems with --track:\n> >   * it's not even in your synopsis\n> >   * user has to repeat themselves (e.g. 'next' in two places from '-c\n> > next --track origin/next'); this repetition is BOTH laborious AND\n> > error-prone\n> >   * it's rather inconsistent: --track is the default due to\n> > auto-vivify when the user specifies nothing but a branch name that\n> > doesn't exist yet, but when the user realizes the branch doesn't exist\n> > yet and asks to have it created then suddenly tracking is not the\n> > default??\n>\n> I don't think --track is default anymore (maybe I haven't updated the\n> man page correctly). The dwim behavior is only activated in\n> switch-branch when you specify --guess to reduce the amount of magic\n> we throw at the user. With that in mind, do we still hide\n> --track/--no-track from switch-branch?\n\nOoh, you're adding --guess?  Cool, that addresses my concerns, just in\na different manner.\n\nPersonally, I'd leave --track/--no-track out.  It's extra mental\noverhead, git branch has options for setting those if they need some\nspecial non-default setup, and if there is enough demand for it we can\nadd it later.  Removing options once published is much harder.\n\n> > I'm not sure what's best, but here's some food for thought:\n> >\n> >\n> >    git switch-branch <branch>\n> > switches to <branch>, if it exists.  Error cases:\n> >   * If <branch> isn't actually a branch but a <tag> or\n> > <remote-tracking-branch> or <revision>, error out and suggest using\n> > --detach.\n> >   * If <branch> isn't actually a branch but there is a similarly named\n> > <remote-tracking-branch> (e.g. origin/<branch>), then suggest using\n> > -c.\n>\n> I would make these advice so I can hide them. Or if I manage to make\n> all these hints one line then I'll make it unconditional.\n>\n> >   git switch-branch -c <branch>\n> > creates <branch> and, if a relevant-remote-tracking branch exists,\n> > base the branch on that revision and set the new branch up to track\n>\n> Hmm.. this is a bit magical and could be surprising. If I create (and\n> switch to) a new branch foo, I don't necessarily mean tracking\n> origin/foo (I may not even think about origin/foo when I type the\n> command). So tentatively no.\n\nYeah, if you're adding --guess then I'm happy.  I do think, though,\nthat if the user runs switch-branch to a branch that doesn't exist, we\nshould check if there is an associated remote-tracking branch so that\nwe can provide a better error message and help users learn about\n--guess.  (Also, will there be a short -g form?)\n\n>\n> > > +If `-C` is given, <new_branch> is created if it doesn't exist;\n> > > +otherwise, it is reset. This is the transactional equivalent of\n> > > ++\n> > > +------------\n> > > +$ git branch -f <branch> [<start_point>]\n> > > +$ git switch-branch <branch>\n> > > +------------\n> > > ++\n> > > +that is to say, the branch is not reset/created unless \"git\n> > > +switch-branch\" is successful.\n> >\n> > ...and when exactly would it fail?  Reading this, it looks like the\n> > only possible error condition was removed due saying we'll reset the\n> > branch if it already exists, so it's rather confusing.\n>\n> Yeah probably just scrape it. The atomic nature is not worth highlighting.\n>\n>\n> > > +'git switch-branch' --detach [<commit>]::\n> > > +\n> > > +       Prepare to work on a unnamed branch on top of <commit> (see\n> > > +       \"DETACHED HEAD\" section), and updating the index and the files\n> > > +       in the working tree.  Local modifications to the files in the\n> > > +       working tree are kept, so that the resulting working tree will\n> > > +       be the state recorded in the commit plus the local\n> > > +       modifications.\n> > > ++\n> > > +When the <commit> argument is a branch name, the `--detach` option can\n> > > +be used to detach HEAD at the tip of the branch (`git switch-branch\n> > > +<branch>` would check out that branch without detaching HEAD).\n> > > ++\n> > > +Omitting <commit> detaches HEAD at the tip of the current branch.\n> > > +\n> > > +OPTIONS\n> > > +-------\n> > > +-q::\n> > > +--quiet::\n> > > +       Quiet, suppress feedback messages.\n> > > +\n> > > +--[no-]progress::\n> > > +       Progress status is reported on the standard error stream\n> > > +       by default when it is attached to a terminal, unless `--quiet`\n> > > +       is specified. This flag enables progress reporting even if not\n> > > +       attached to a terminal, regardless of `--quiet`.\n> > > +\n> > > +-f::\n> > > +--force::\n> > > +       Proceed even if the index or the working tree differs from\n> > > +       HEAD.  This is used to throw away local changes.\n> >\n> > Haven't thought through this thoroughly, but do we really need an\n> > option for that instead of telling users to 'git reset --hard HEAD'\n> > before switching branches if they want their stuff thrown away?\n>\n> For me it's just a bit more convenient. Hit an error when switching\n> branch? Recall the command from bash history, stick -f in it and run.\n> Elsewhere I think both Junio and Thomas (or maybe only Junio) suggests\n> moving the \"git reset\" functionality without moving HEAD to one of\n> these commands, which goes the opposite direction...\n\nFair enough.\n\n> > > +-c <new_branch>::\n> > > +--create <new_branch>::\n> > > +       Create a new branch named <new_branch> and start it at\n> > > +       <start_point>; see linkgit:git-branch[1] for details.\n> > > +\n> > > +-C <new_branch>::\n> > > +--force-create <new_branch>::\n> > > +       Creates the branch <new_branch> and start it at <start_point>;\n> > > +       if it already exists, then reset it to <start_point>. This is\n> > > +       equivalent to running \"git branch\" with \"-f\"; see\n> > > +       linkgit:git-branch[1] for details.\n> >\n> > Makes sense, but let's get the -b/-B vs. -c/-C consistent.\n>\n> Another option I'm considering is -n/-N (for _new_ branch). Maybe\n> -c/-C is good enough.\n\nActually, -n/-N seems like a good idea, especially since --guess also\ncreates a branch.  If we do stick with -c/-C, then we may need to\ndocument --guess as implying -c to avoid confusion (and then make sure\nwe get the synopsis correct to show which flags can be used together).\n\n> > > +-l::\n> > > +       Create the new branch's reflog; see linkgit:git-branch[1] for\n> > > +       details.\n> >\n> > ??  Jettison this.\n>\n> Yep. It looks weird to me too. reflog is just behind the scene these\n> days. Nobody need to explicitly ask for reflog anymore.\n>\n> > > +--orphan <new_branch>::\n> > > +       Create a new 'orphan' branch, named <new_branch>, started from\n> > > +       <start_point> and switch to it.  The first commit made on this\n> >\n> > What??  started from <start_point>?  The whole point of --orphan is\n> > you have no parent, i.e. no start point.  Also, why does the\n> > explanation reference an argument that wasn't in the immediately\n> > preceding synopsis?\n>\n> I guess bad phrasing. It should be \"switch to <start_point> first,\n> then prepare the worktree so that the first commit will have no\n> parent\". Or something along that line.\n>\n> You should really review git-checkout.txt btw ;-)\n\nI did after writing several of these comments, and yeah, it really\nneeds a clean up.  Seems like something someone would do when writing\na (partial) replacement or simplified alternative.  ;-)\n\nTo be fair though, I suspect anyone familiar enough with git that\nlooks at git-checkout.txt again is probably going to miss at least one\nthing that is bad for new users no matter how closely they look, just\nbecause they've grown accustomed to the documentation as it is.  I was\ntrying to help point out possible issues that I spotted, but I suspect\nothers may be able to point out more.\n\n> > > +       new branch will have no parents and it will be the root of a new\n> > > +       history totally disconnected from all the other branches and\n> > > +       commits.\n> > > ++\n> > > +The index and the working tree are adjusted as if you had previously run\n> > > +\"git checkout <start_point>\".  This allows you to start a new history\n> > > +that records a set of paths similar to <start_point> by easily running\n> > > +\"git commit -a\" to make the root commit.\n> > > ++\n> > > +This can be useful when you want to publish the tree from a commit\n> > > +without exposing its full history. You might want to do this to publish\n> > > +an open source branch of a project whose current tree is \"clean\", but\n> > > +whose full history contains proprietary or otherwise encumbered bits of\n> > > +code.\n> > > ++\n> > > +If you want to start a disconnected history that records a set of paths\n> > > +that is totally different from the one of <start_point>, then you should\n> > > +clear the index and the working tree right after creating the orphan\n> > > +branch by running \"git rm -rf .\" from the top level of the working tree.\n> > > +Afterwards you will be ready to prepare your new files, repopulating the\n> > > +working tree, by copying them from elsewhere, extracting a tarball, etc.\n> >\n> > Ick.  Seems overly complex.  I'd rather that --orphan defaulted to\n> > clearing the index and working tree, and that one would need to pass\n> > HEAD for <start_point> if you wanted to start out with all those other\n> > files.  That would certainly make the explanation a little clearer to\n> > users, and more natural when they start experimenting with it.\n> >\n> > However, --orphan is pretty special case.  Do we perhaps want to leave\n> > it out of this new command and only include it in checkout?\n>\n> I started this by simply splitting git-checkout in two commands that,\n> combined, can do everything git-checkout can. Then suggestions to have\n> better default came in and I think we started to drift further to\n> _removing_ options and falling back to git-checkout.\n>\n> I think we could still keep \"complicated\" options as long as they are\n> clearly described and don't surprise users until they figure them out.\n> That way I don't have to go back to git-checkout and deal with all the\n> ambiguation it creates.\n\nFair enough...though I think it may make sense to also review the\ncomplicated options and determine if they are overly complicated.  I\nthink --orphan qualifies (I stumbled with it a bit for years the\noccasional time I needed to use it), and my small suggestion above\nwould simplify both it and its description.  We should probably also\nconsider just removing <start_point> as an acceptable argument to\n--orphan; if people want files from some revision after creating an\norphan branch that's a simple extra command.\n\n> > > +-m::\n> > > +--merge::\n> > > +       If you have local modifications to one or more files that are\n> > > +       different between the current branch and the branch to which\n> > > +       you are switching, the command refuses to switch branches in\n> > > +       order to preserve your modifications in context.  However,\n> > > +       with this option, a three-way merge between the current\n> > > +       branch, your working tree contents, and the new branch is\n> > > +       done, and you will be on the new branch.\n> > > ++\n> > > +When a merge conflict happens, the index entries for conflicting\n> > > +paths are left unmerged, and you need to resolve the conflicts\n> > > +and mark the resolved paths with `git add` (or `git rm` if the merge\n> > > +should result in deletion of the path).\n> > > +\n> > > +--conflict=<style>::\n> > > +       The same as --merge option above, but changes the way the\n> > > +       conflicting hunks are presented, overriding the\n> > > +       merge.conflictStyle configuration variable.  Possible values are\n> > > +       \"merge\" (default) and \"diff3\" (in addition to what is shown by\n> > > +       \"merge\" style, shows the original contents).\n> > > +\n> > > +--ignore-other-worktrees::\n> > > +       `git switch-branch` refuses when the wanted ref is already\n> > > +       checked out by another worktree. This option makes it check\n> > > +       the ref out anyway. In other words, the ref can be held by\n> > > +       more than one worktree.\n> >\n> > seems rather dangerous...is the goal to be an easier-to-use suggestion\n> > for new users while checkout continues to exist, or is this command\n> > meant to handle all branch switching functionality that checkout has?\n>\n> As explained above. I'm still thinking the latter, but with fewer\n> surprises and confusion. Though I guess I could be convinced to go\n> with the former (the problem with the former is, even as a\n> no-longer-new user, I still find git-checkout not that pleasant to use\n> and want a better replacement)\n>\n> > > +<branch>::\n> > > +       Branch to checkout; if it refers to a branch (i.e., a name that,\n> > > +       when prepended with \"refs/heads/\", is a valid ref), then that\n> > > +       branch is checked out. Otherwise, if it refers to a valid\n> > > +       commit, your HEAD becomes \"detached\" and you are no longer on\n> > > +       any branch (see below for details).\n> >\n> > I thought we requiring --detach in order to detach.  Does this\n> > paragraph need updating?  Also, if we require --detach when we'll be\n> > detaching HEAD, then this paragraph gets a LOT simpler.\n>\n> Yep. I really need to read through the document and update all of it.\n>\n> > > +You can use the `\"@{-N}\"` syntax to refer to the N-th last\n> > > +branch/commit checked out using \"git checkout\" operation. You may\n> > > +also specify `-` which is synonymous to `\"@{-1}`.\n> > > ++\n> > > +As a special case, you may use `\"A...B\"` as a shortcut for the\n> > > +merge base of `A` and `B` if there is exactly one merge base. You can\n> > > +leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.\n> >\n> > I actually didn't know about the A...B special case for checkout.\n> > Interesting...but I'm starting to wonder if this is too much info for\n> > a \"simplified command\".\n>\n> I could just hint about A...B and send the user to git-checkout.txt if\n> they need to know more. They can learn about git-checkout that way\n> too.\n\nIt may be fine to stay.  Lots of my comments were just \"let's try to\nnote any weirdness or complication that might seem excessive so we at\nleast have a conversation about it\" (because it's easy to gloss over\nsince we've looked at these documents so many times) rather than a\n\"this definitely needs to go\".\n"},{"id":"364567","messageId":"CABPp-BH=rsLqq4ZRMSUv6n0n5p=aMZs-+VkVT=7P8n4=iUk=-Q@mail.gmail.com","threadId":"49793","inReplyTo":"CACsJy8DEMHFTnL2QJu5Csb1jUQeu0HiT3rTDii4krrEJcoh=Qw@mail.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2018-12-04T17:45:08Z","receivedAt":"2018-12-04T17:45:23Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Dec 4, 2018 at 8:28 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Tue, Dec 4, 2018 at 2:29 AM Elijah Newren <newren@gmail.com> wrote:\n> >\n> > On Thu, Nov 29, 2018 at 2:01 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > >\n> > > v3 sees switch-branch go back to switch-branch (in v2 it was\n> > > checkout-branch). checkout-files is also renamed restore-files (v1 was\n> > > restore-paths). Hopefully we won't see another rename.\n> >\n> > I started reading through the patches.  I also tried to apply them\n> > locally, but they had conflicts or missing base file version on both\n> > master and next.  What version did you base it on?\n>\n> I think nd/checkout-dwim-fix because of a non-trivial conflict there\n> (but I don't remember when I noticed it and rebased on that). Anyway\n> you can get the whole series at\n>\n> https://gitlab.com/pclouds/git/tree/switch-branch-and-checkout-files\n>\n> It fixes some of your comments already, a couple of bug fixes here and\n> there and in a good-enough shape that I start actually using it.\n\nCool.\n\n> > > - Two more fancy features (the \"git checkout --index\" being the\n> > >   default mode and the backup log for accidental overwrites) are of\n> > >   course still missing. But they are coming.\n> > >\n> > > I did not go replace \"detached HEAD\" with \"unnamed branch\" (or \"no\n> > > branch\") everywhere because I think a unique term is still good to\n> > > refer to this concept. Or maybe \"no branch\" is good enough. I dunno.\n> >\n> > I personally like \"unnamed branch\", but \"no branch\" would still be\n> > better than \"detached HEAD\".\n>\n> Haven't really worked on killing the term \"detached HEAD\" yet. But I\n> noticed the other day that git-branch reports\n>\n> * (HEAD detached from 703266f6e4)\n>\n> and I didn't know how to rephrase that. I guess \"unnamed branch from\n> 703266f6e4\" is probably good enough but my old-timer brain screams no.\n\nPerhaps \"* (On an unnamed branch, at 703266f6e4)\"?\n"},{"id":"364569","messageId":"CACsJy8D9Rgsf-E6yweQxpopFaOVZ1bgihEbg200yS1gup+Gt7Q@mail.gmail.com","threadId":"49793","inReplyTo":"CABPp-BGRcaiiD-aks1kaLr7ATLQ_oGSyooQBDD+2acgerA+Phg@mail.gmail.com","subject":"Re: [PATCH v3 07/14] checkout: split into switch-branch and restore-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-12-04T18:17:39Z","receivedAt":"2018-12-04T18:18:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Dec 4, 2018 at 6:43 PM Elijah Newren <newren@gmail.com> wrote:\n> > > > +--ours::\n> > > > +--theirs::\n> > > > +       Check out stage #2 ('ours') or #3 ('theirs') for unmerged\n> > > > +       paths.\n> > > > ++\n> > > > +Note that during `git rebase` and `git pull --rebase`, 'ours' and\n> > > > +'theirs' may appear swapped; `--ours` gives the version from the\n> > > > +branch the changes are rebased onto, while `--theirs` gives the\n> > > > +version from the branch that holds your work that is being rebased.\n> > > > ++\n> > > > +This is because `rebase` is used in a workflow that treats the\n> > > > +history at the remote as the shared canonical one, and treats the\n> > > > +work done on the branch you are rebasing as the third-party work to\n> > > > +be integrated, and you are temporarily assuming the role of the\n> > > > +keeper of the canonical history during the rebase.  As the keeper of\n> > > > +the canonical history, you need to view the history from the remote\n> > > > +as `ours` (i.e. \"our shared canonical history\"), while what you did\n> > > > +on your side branch as `theirs` (i.e. \"one contributor's work on top\n> > > > +of it\").\n> > >\n> > > Total aside because I'm not sure what you could change here, but man\n> > > do I hate this.\n> >\n> > Uh it's actually documented? I'm always confused by this too. --ours\n> > and --theirs at this point are pretty much tied to stage 2 and 3.\n> > Nothing I can do about it. But if you could come up with some other\n> > option names, then we could make \"new ours\" to be stage 3 during\n> > rebase, for example.\n>\n> I don't think it's a naming issue, personally.  Years ago we could\n> have defined --ours and --theirs differently based on which kind of\n> operation we were in the middle of, but you are probably right that\n> they are now tied to stage 2 and 3.  But there's another place that we\n> might still be able to address this; I think the brain-damage here may\n> have just been due to the fact that the recursive merge machinery was\n> rather inflexible and required HEAD to be stage 2.  If it were a\n> little more flexible, then we might just be able to make this problem\n> go away.  Maybe it can still be fixed (I haven't dug too deeply into\n> it), but if so, the only fix needed here would be to remove this long\n> explanation about why the tool gets things totally backward.\n\nAha. I' not really deep in this merge business to know if stages 2 and\n3 can be swapped. This is right up your alley. I'll just leave it to\nyou.\n\n> > > > +'git switch-branch' -c|-C <new_branch> [<start_point>]::\n> > > > +\n> > > > +       Specifying `-c` causes a new branch to be created as if\n> > > > +       linkgit:git-branch[1] were called and then switched to. In\n> > > > +       this case you can use the `--track` or `--no-track` options,\n> > > > +       which will be passed to 'git branch'.  As a convenience,\n> > > > +       `--track` without `-c` implies branch creation; see the\n> > > > +       description of `--track` below.\n> > >\n> > > Can we get rid of --track/--no-track and just provide a flag (which\n> > > takes no arguments) for the user to use?  Problems with --track:\n> > >   * it's not even in your synopsis\n> > >   * user has to repeat themselves (e.g. 'next' in two places from '-c\n> > > next --track origin/next'); this repetition is BOTH laborious AND\n> > > error-prone\n> > >   * it's rather inconsistent: --track is the default due to\n> > > auto-vivify when the user specifies nothing but a branch name that\n> > > doesn't exist yet, but when the user realizes the branch doesn't exist\n> > > yet and asks to have it created then suddenly tracking is not the\n> > > default??\n> >\n> > I don't think --track is default anymore (maybe I haven't updated the\n> > man page correctly). The dwim behavior is only activated in\n> > switch-branch when you specify --guess to reduce the amount of magic\n> > we throw at the user. With that in mind, do we still hide\n> > --track/--no-track from switch-branch?\n>\n> Ooh, you're adding --guess?  Cool, that addresses my concerns, just in\n> a different manner.\n\nNo it's always there even in git-checkout, just hidden.\n\n> Personally, I'd leave --track/--no-track out.  It's extra mental\n> overhead, git branch has options for setting those if they need some\n> special non-default setup, and if there is enough demand for it we can\n> add it later.  Removing options once published is much harder.\n\nSlightly less convenient (you would need a combination of git-branch\nand git-switch-branch, if you avoid git-checkout). But since I don't\nthink I have ever used this option, I'm fine with leaving it out and\nconsidering adding it back later.\n\n> > > I'm not sure what's best, but here's some food for thought:\n> > >\n> > >\n> > >    git switch-branch <branch>\n> > > switches to <branch>, if it exists.  Error cases:\n> > >   * If <branch> isn't actually a branch but a <tag> or\n> > > <remote-tracking-branch> or <revision>, error out and suggest using\n> > > --detach.\n> > >   * If <branch> isn't actually a branch but there is a similarly named\n> > > <remote-tracking-branch> (e.g. origin/<branch>), then suggest using\n> > > -c.\n> >\n> > I would make these advice so I can hide them. Or if I manage to make\n> > all these hints one line then I'll make it unconditional.\n> >\n> > >   git switch-branch -c <branch>\n> > > creates <branch> and, if a relevant-remote-tracking branch exists,\n> > > base the branch on that revision and set the new branch up to track\n> >\n> > Hmm.. this is a bit magical and could be surprising. If I create (and\n> > switch to) a new branch foo, I don't necessarily mean tracking\n> > origin/foo (I may not even think about origin/foo when I type the\n> > command). So tentatively no.\n>\n> Yeah, if you're adding --guess then I'm happy.  I do think, though,\n> that if the user runs switch-branch to a branch that doesn't exist, we\n> should check if there is an associated remote-tracking branch so that\n> we can provide a better error message and help users learn about\n> --guess.  (Also, will there be a short -g form?)\n\nYeah better error and suggestion is a good idea. And yes the short\nform -g is already added (I did try to use it and find --guess too\ntime consuming even with bash completion support).\n\n> > > > +-f::\n> > > > +--force::\n> > > > +       Proceed even if the index or the working tree differs from\n> > > > +       HEAD.  This is used to throw away local changes.\n> > >\n> > > Haven't thought through this thoroughly, but do we really need an\n> > > option for that instead of telling users to 'git reset --hard HEAD'\n> > > before switching branches if they want their stuff thrown away?\n> >\n> > For me it's just a bit more convenient. Hit an error when switching\n> > branch? Recall the command from bash history, stick -f in it and run.\n> > Elsewhere I think both Junio and Thomas (or maybe only Junio) suggests\n> > moving the \"git reset\" functionality without moving HEAD to one of\n> > these commands, which goes the opposite direction...\n>\n> Fair enough.\n\nI'm actually still not sure how to move it here (I guess 'here' is\nrestore-files since we won't move HEAD). All the --mixed, --merge and\n--hard are confusing. But maybe we could just make 'git restore-files\n--from HEAD -f :/\" behave just like \"git reset --hard HEAD\" (but with\nsome safety net) But we can leave it for discussion in the next round.\n\n> > > > +--orphan <new_branch>::\n> > > > +       Create a new 'orphan' branch, named <new_branch>, started from\n> > > > +       <start_point> and switch to it.  The first commit made on this\n> > >\n> > > What??  started from <start_point>?  The whole point of --orphan is\n> > > you have no parent, i.e. no start point.  Also, why does the\n> > > explanation reference an argument that wasn't in the immediately\n> > > preceding synopsis?\n> >\n> > I guess bad phrasing. It should be \"switch to <start_point> first,\n> > then prepare the worktree so that the first commit will have no\n> > parent\". Or something along that line.\n> >\n> > You should really review git-checkout.txt btw ;-)\n>\n> I did after writing several of these comments, and yeah, it really\n> needs a clean up.  Seems like something someone would do when writing\n> a (partial) replacement or simplified alternative.  ;-)\n\nHeh ;-) Fine I'll do it. I have to read and re-read git-checkout.txt\nanyway and already queued up a couple small fixes.\n\n> > > > +       new branch will have no parents and it will be the root of a new\n> > > > +       history totally disconnected from all the other branches and\n> > > > +       commits.\n> > > > ++\n> > > > +The index and the working tree are adjusted as if you had previously run\n> > > > +\"git checkout <start_point>\".  This allows you to start a new history\n> > > > +that records a set of paths similar to <start_point> by easily running\n> > > > +\"git commit -a\" to make the root commit.\n> > > > ++\n> > > > +This can be useful when you want to publish the tree from a commit\n> > > > +without exposing its full history. You might want to do this to publish\n> > > > +an open source branch of a project whose current tree is \"clean\", but\n> > > > +whose full history contains proprietary or otherwise encumbered bits of\n> > > > +code.\n> > > > ++\n> > > > +If you want to start a disconnected history that records a set of paths\n> > > > +that is totally different from the one of <start_point>, then you should\n> > > > +clear the index and the working tree right after creating the orphan\n> > > > +branch by running \"git rm -rf .\" from the top level of the working tree.\n> > > > +Afterwards you will be ready to prepare your new files, repopulating the\n> > > > +working tree, by copying them from elsewhere, extracting a tarball, etc.\n> > >\n> > > Ick.  Seems overly complex.  I'd rather that --orphan defaulted to\n> > > clearing the index and working tree, and that one would need to pass\n> > > HEAD for <start_point> if you wanted to start out with all those other\n> > > files.  That would certainly make the explanation a little clearer to\n> > > users, and more natural when they start experimenting with it.\n> > >\n> > > However, --orphan is pretty special case.  Do we perhaps want to leave\n> > > it out of this new command and only include it in checkout?\n> >\n> > I started this by simply splitting git-checkout in two commands that,\n> > combined, can do everything git-checkout can. Then suggestions to have\n> > better default came in and I think we started to drift further to\n> > _removing_ options and falling back to git-checkout.\n> >\n> > I think we could still keep \"complicated\" options as long as they are\n> > clearly described and don't surprise users until they figure them out.\n> > That way I don't have to go back to git-checkout and deal with all the\n> > ambiguation it creates.\n>\n> Fair enough...though I think it may make sense to also review the\n> complicated options and determine if they are overly complicated.  I\n> think --orphan qualifies (I stumbled with it a bit for years the\n> occasional time I needed to use it), and my small suggestion above\n> would simplify both it and its description.  We should probably also\n> consider just removing <start_point> as an acceptable argument to\n> --orphan; if people want files from some revision after creating an\n> orphan branch that's a simple extra command.\n\nIt is good that you pointed it out though. I still have to digest this\noption before I make more comments, but generally if there's a simpler\n(even if new) way to achieve the same thing, I'm all for it.\n-- \nDuy\n"},{"id":"364570","messageId":"CACsJy8BSm945_hqwT3MSW2H_1so1KwrW_p1zz3V-fObwyGNUjw@mail.gmail.com","threadId":"49793","inReplyTo":"CABPp-BH=rsLqq4ZRMSUv6n0n5p=aMZs-+VkVT=7P8n4=iUk=-Q@mail.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-12-04T18:22:25Z","receivedAt":"2018-12-04T18:22:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Dec 4, 2018 at 6:45 PM Elijah Newren <newren@gmail.com> wrote:\n> > > > - Two more fancy features (the \"git checkout --index\" being the\n> > > >   default mode and the backup log for accidental overwrites) are of\n> > > >   course still missing. But they are coming.\n> > > >\n> > > > I did not go replace \"detached HEAD\" with \"unnamed branch\" (or \"no\n> > > > branch\") everywhere because I think a unique term is still good to\n> > > > refer to this concept. Or maybe \"no branch\" is good enough. I dunno.\n> > >\n> > > I personally like \"unnamed branch\", but \"no branch\" would still be\n> > > better than \"detached HEAD\".\n> >\n> > Haven't really worked on killing the term \"detached HEAD\" yet. But I\n> > noticed the other day that git-branch reports\n> >\n> > * (HEAD detached from 703266f6e4)\n> >\n> > and I didn't know how to rephrase that. I guess \"unnamed branch from\n> > 703266f6e4\" is probably good enough but my old-timer brain screams no.\n>\n> Perhaps \"* (On an unnamed branch, at 703266f6e4)\"?\n\nThis 703266f6e4 is the fork point. Once you start adding more commits\non top of this unnamed branch, I find it hard to define it \"at\"\n703266f6e4 anymore. \"forked from 703266f6e4\" (or even starting/growing\nfrom...) is probably clearest but also a bit longer.\n-- \nDuy\n"},{"id":"364571","messageId":"CABPp-BFk8XXv=6bu0XPFfiDrNWE4HP9qF=5E+QFx3Q-brj=BBw@mail.gmail.com","threadId":"49793","inReplyTo":"CACsJy8BSm945_hqwT3MSW2H_1so1KwrW_p1zz3V-fObwyGNUjw@mail.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2018-12-04T18:31:06Z","receivedAt":"2018-12-04T18:31:22Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Dec 4, 2018 at 10:22 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Tue, Dec 4, 2018 at 6:45 PM Elijah Newren <newren@gmail.com> wrote:\n> > > > > - Two more fancy features (the \"git checkout --index\" being the\n> > > > >   default mode and the backup log for accidental overwrites) are of\n> > > > >   course still missing. But they are coming.\n> > > > >\n> > > > > I did not go replace \"detached HEAD\" with \"unnamed branch\" (or \"no\n> > > > > branch\") everywhere because I think a unique term is still good to\n> > > > > refer to this concept. Or maybe \"no branch\" is good enough. I dunno.\n> > > >\n> > > > I personally like \"unnamed branch\", but \"no branch\" would still be\n> > > > better than \"detached HEAD\".\n> > >\n> > > Haven't really worked on killing the term \"detached HEAD\" yet. But I\n> > > noticed the other day that git-branch reports\n> > >\n> > > * (HEAD detached from 703266f6e4)\n> > >\n> > > and I didn't know how to rephrase that. I guess \"unnamed branch from\n> > > 703266f6e4\" is probably good enough but my old-timer brain screams no.\n> >\n> > Perhaps \"* (On an unnamed branch, at 703266f6e4)\"?\n>\n> This 703266f6e4 is the fork point. Once you start adding more commits\n> on top of this unnamed branch, I find it hard to define it \"at\"\n> 703266f6e4 anymore. \"forked from 703266f6e4\" (or even starting/growing\n> from...) is probably clearest but also a bit longer.\n\nIt reports the fork point rather than the commit HEAD points to?  Ah,\nI guess I never payed that close of attention before.  I actually\nthink \"on an unnamed branch\" is good enough, but if others gain value\nfrom the extra info, then I understand the conundrum.  I'm not sure\nwhat the use or rationale is for the fork point, though, so I feel\nslightly at a loss to try to describe this extra piece of info.\n"},{"id":"364572","messageId":"CACsJy8BfOwXvO1dvuFB+3vY2JAaErD8x0NDcVcpvPtBEw9QDew@mail.gmail.com","threadId":"49793","inReplyTo":"CABPp-BFk8XXv=6bu0XPFfiDrNWE4HP9qF=5E+QFx3Q-brj=BBw@mail.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-12-04T18:39:31Z","receivedAt":"2018-12-04T18:40:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Dec 4, 2018 at 7:31 PM Elijah Newren <newren@gmail.com> wrote:\n>\n> On Tue, Dec 4, 2018 at 10:22 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> >\n> > On Tue, Dec 4, 2018 at 6:45 PM Elijah Newren <newren@gmail.com> wrote:\n> > > > > > - Two more fancy features (the \"git checkout --index\" being the\n> > > > > >   default mode and the backup log for accidental overwrites) are of\n> > > > > >   course still missing. But they are coming.\n> > > > > >\n> > > > > > I did not go replace \"detached HEAD\" with \"unnamed branch\" (or \"no\n> > > > > > branch\") everywhere because I think a unique term is still good to\n> > > > > > refer to this concept. Or maybe \"no branch\" is good enough. I dunno.\n> > > > >\n> > > > > I personally like \"unnamed branch\", but \"no branch\" would still be\n> > > > > better than \"detached HEAD\".\n> > > >\n> > > > Haven't really worked on killing the term \"detached HEAD\" yet. But I\n> > > > noticed the other day that git-branch reports\n> > > >\n> > > > * (HEAD detached from 703266f6e4)\n> > > >\n> > > > and I didn't know how to rephrase that. I guess \"unnamed branch from\n> > > > 703266f6e4\" is probably good enough but my old-timer brain screams no.\n> > >\n> > > Perhaps \"* (On an unnamed branch, at 703266f6e4)\"?\n> >\n> > This 703266f6e4 is the fork point. Once you start adding more commits\n> > on top of this unnamed branch, I find it hard to define it \"at\"\n> > 703266f6e4 anymore. \"forked from 703266f6e4\" (or even starting/growing\n> > from...) is probably clearest but also a bit longer.\n>\n> It reports the fork point rather than the commit HEAD points to?  Ah,\n> I guess I never payed that close of attention before.  I actually\n> think \"on an unnamed branch\" is good enough, but if others gain value\n> from the extra info, then I understand the conundrum.  I'm not sure\n> what the use or rationale is for the fork point, though, so I feel\n> slightly at a loss to try to describe this extra piece of info.\n\nIt's probably a corner case. This is a better example\n\n* (HEAD detached at pclouds/backup-log)\n\nIt does help see i'm working on top of some branch (or tag)\n-- \nDuy\n"},{"id":"364577","messageId":"CAPig+cRnQaaACTi3VRrW6-t+mwqhggTd72DQd1s3uKzAEwR9tQ@mail.gmail.com","threadId":"49793","inReplyTo":"CACsJy8DEMHFTnL2QJu5Csb1jUQeu0HiT3rTDii4krrEJcoh=Qw@mail.gmail.com","subject":"Re: [PATCH/RFC v3 00/14] Introduce new commands switch-branch and restore-files","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-12-04T21:18:34Z","receivedAt":"2018-12-04T21:18:48Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Dec 4, 2018 at 11:28 AM Duy Nguyen <pclouds@gmail.com> wrote:\n> Haven't really worked on killing the term \"detached HEAD\" yet. But I\n> noticed the other day that git-branch reports\n>\n> * (HEAD detached from 703266f6e4)\n>\n> and I didn't know how to rephrase that. I guess \"unnamed branch from\n> 703266f6e4\" is probably good enough but my old-timer brain screams no.\n\n\"git worktree add\" and \"git worktree show\" also report similar messages.\n"},{"id":"364588","messageId":"xmqqtvjsen3r.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CACsJy8BTs+WKzTTEF2XVTT-LVJk_exYCz_hN+hXU1Dw+oquBpA@mail.gmail.com","subject":"Re: [PATCH v3 07/14] checkout: split into switch-branch and restore-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-12-05T02:14:32Z","receivedAt":"2018-12-05T02:14:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> My single biggest worry about this whole series is that I'm worried\n>> you're perpetuating and even further ingraining one of the biggest\n>> usability problems with checkout: people suggest and use it for\n>> reverting/restoring paths to a previous version, but it doesn't do\n>> that:\n>\n> ...\n>\n>  git restore-files --from=master~10 Documentation/\n\nThe \"single biggest worry\" could be due to Elijah not being aware of\nother recent discussions.  My understanding of the plan is\n\n - \"git checkout\" will learn a new \"--[no-]overlay\" option, where\n   the current behaviour, i.e. \"take paths in master~10 that match\n   pathspec Documentation/, and overlay them on top of what is in\n   the index and the working tree\", is explained as \"the overlay\n   mode\" and stays to be the default.  With \"checkout --no-overlay\n   master~10 Documentation/\", the command will become \"replace paths\n   in the current index and the working tree that match the pathspec\n   Documentation/ with paths in master~10 that match pathspec\n   Documentation/\".\n\n - \"git restore-files --from=<tree> <pathspec>\" by default will use\n   \"--no-overlay\" semantics, but the users can still use \"--overlay\"\n   from the command line as an option.\n\nSo \"restore-files\" would become truly \"restore the state of\nDocumentation/ to match that of master~10\", I would think.\n\n>> Also, the fact that we're trying to make a simpler command makes me\n>> think that removing the auto-vivify behavior from the default and\n>> adding a simple flag which users can pass to request will allow this\n>> part of the documentation to be hidden behind the appropriate flag,\n>> which may make it easier for users to compartmentalize the command and\n>> it's options, enabling them to learn as they go.\n>\n> Sounds good. I don't know a good name for this new option though so\n> unless anybody comes up with some suggestion, I'll just disable\n> checkout.defaultRemote in switch-branch. If it comes back as a new\n> option, it can always be added later.\n\nAre you two discussing the \"checkout --guess\" option?  I am somewhat\nlost here.\n\n>> > +-f::\n>> > +--force::\n>> > +       Proceed even if the index or the working tree differs from\n>> > +       HEAD.  This is used to throw away local changes.\n>>\n>> Haven't thought through this thoroughly, but do we really need an\n>> option for that instead of telling users to 'git reset --hard HEAD'\n>> before switching branches if they want their stuff thrown away?\n>\n> For me it's just a bit more convenient. Hit an error when switching\n> branch? Recall the command from bash history, stick -f in it and run.\n> Elsewhere I think both Junio and Thomas (or maybe only Junio) suggests\n> moving the \"git reset\" functionality without moving HEAD to one of\n> these commands, which goes the opposite direction...\n\nIsn't there a huge difference?  \"checkout --force <other-branch>\"\nneeds to clobber only the changes that are involved in the switch,\ni.e. if your README.txt is the same between master and maint while\nMakefile is different, after editing both files while on master, you\ncan not \"switch-branch\" to maint without doing something to Makefile\n(i.e. either discard your local change or wiggle your local change\nto the context of 'maint' with \"checkout -m\").  But you can carry\nthe changes to README.txt while checking out 'maint' branch.\nRunning \"git reset --hard HEAD\" would mean that you will lose the\nchanges to README.txt as well.\n\n>> > +--orphan <new_branch>::\n>> > +       Create a new 'orphan' branch, named <new_branch>, started from\n>> > +       <start_point> and switch to it.  The first commit made on this\n>>\n>> What??  started from <start_point>?  The whole point of --orphan is\n>> you have no parent, i.e. no start point.  Also, why does the\n>> explanation reference an argument that wasn't in the immediately\n>> preceding synopsis?\n>\n> I guess bad phrasing. It should be \"switch to <start_point> first,\n> then prepare the worktree so that the first commit will have no\n> parent\". Or something along that line.\n\nIt should be a <tree-ish>, no?  It is not a \"point\" in history, but\nis \"start with this tree\".\n\nI may have more comments on this message but that's it from me for\nnow.\n"},{"id":"364589","messageId":"xmqqpnugemks.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CACsJy8D9Rgsf-E6yweQxpopFaOVZ1bgihEbg200yS1gup+Gt7Q@mail.gmail.com","subject":"Re: [PATCH v3 07/14] checkout: split into switch-branch and restore-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-12-05T02:25:55Z","receivedAt":"2018-12-05T02:26:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Tue, Dec 4, 2018 at 6:43 PM Elijah Newren <newren@gmail.com> wrote:\n>> > > > +--ours::\n>> > > > +--theirs::\n>> ...\n>> go away.  Maybe it can still be fixed (I haven't dug too deeply into\n>> it), but if so, the only fix needed here would be to remove this long\n>> explanation about why the tool gets things totally backward.\n>\n> Aha. I' not really deep in this merge business to know if stages 2 and\n> 3 can be swapped. This is right up your alley. I'll just leave it to\n> you.\n\nPlease don't show stage#2 and stage#3 swapped to the end user,\nunless that is protected behind an option (not per-repo config).\nIt is pretty much ingrained that stage#2 is what came from the\ncommit that will become the first parent of the commit being\nprepared, and changing it without an explicit command line option\nwill break tools.\n\n> I'm actually still not sure how to move it here (I guess 'here' is\n> restore-files since we won't move HEAD). All the --mixed, --merge and\n> --hard are confusing. But maybe we could just make 'git restore-files\n> --from HEAD -f :/\" behave just like \"git reset --hard HEAD\" (but with\n> some safety net) But we can leave it for discussion in the next round.\n\nPerhaps you two should pay a bit closer attention to what Thomas\nGummerer is working on.  I've touched above in my earlier comments,\ntoo, e.g.  <xmqqefb3mhrs.fsf@gitster-ct.c.googlers.com>\n"},{"id":"364599","messageId":"CABPp-BHnTqWjxfAYtjyoGsioBKPHcTfxK3jFaBDWCK4MasXDMQ@mail.gmail.com","threadId":"49793","inReplyTo":"xmqqtvjsen3r.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3 07/14] checkout: split into switch-branch and restore-files","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2018-12-05T04:22:43Z","receivedAt":"2018-12-05T04:22:58Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Dec 4, 2018 at 6:14 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n> >> My single biggest worry about this whole series is that I'm worried\n> >> you're perpetuating and even further ingraining one of the biggest\n> >> usability problems with checkout: people suggest and use it for\n> >> reverting/restoring paths to a previous version, but it doesn't do\n> >> that:\n> >\n> > ...\n> >\n> >  git restore-files --from=master~10 Documentation/\n>\n> The \"single biggest worry\" could be due to Elijah not being aware of\n> other recent discussions.  My understanding of the plan is\n>\n>  - \"git checkout\" will learn a new \"--[no-]overlay\" option, where\n>    the current behaviour, i.e. \"take paths in master~10 that match\n>    pathspec Documentation/, and overlay them on top of what is in\n>    the index and the working tree\", is explained as \"the overlay\n>    mode\" and stays to be the default.  With \"checkout --no-overlay\n>    master~10 Documentation/\", the command will become \"replace paths\n>    in the current index and the working tree that match the pathspec\n>    Documentation/ with paths in master~10 that match pathspec\n>    Documentation/\".\n>\n>  - \"git restore-files --from=<tree> <pathspec>\" by default will use\n>    \"--no-overlay\" semantics, but the users can still use \"--overlay\"\n>    from the command line as an option.\n>\n> So \"restore-files\" would become truly \"restore the state of\n> Documentation/ to match that of master~10\", I would think.\n\nOh, sweet, that's awesome.  I saw some of the other emails as I was\nscanning through and looked at the --overlay stuff since it sounded\nrelevant but something in the explanation made me think it was about\nwhether the command would write to the index or the working tree,\nrather than being about adding+overwriting vs. just overwriting.\n\n> >> Also, the fact that we're trying to make a simpler command makes me\n> >> think that removing the auto-vivify behavior from the default and\n> >> adding a simple flag which users can pass to request will allow this\n> >> part of the documentation to be hidden behind the appropriate flag,\n> >> which may make it easier for users to compartmentalize the command and\n> >> it's options, enabling them to learn as they go.\n> >\n> > Sounds good. I don't know a good name for this new option though so\n> > unless anybody comes up with some suggestion, I'll just disable\n> > checkout.defaultRemote in switch-branch. If it comes back as a new\n> > option, it can always be added later.\n>\n> Are you two discussing the \"checkout --guess\" option?  I am somewhat\n> lost here.\n\nGenerally what was being discussed was just that this manpage was\nrather complicated for the standard base-case due to the need to\nexplain all the behavior associated with --guess since that option is\non by default in checkout.  And since --guess is controlled by\ncheckout.defaultRemote, that was part of the extra complexity that had\nto be learned in the basic explanation, rather than letting users\nlearn it when they learn a new flag.\n\nThe critical part you were missing was part of the original text just\nbefore the quoted part was:\n\n>>> So switch-branch will be controlled by checkout.* config variables?\n>>> That probably makes the most sense, but it does dilute the advantage\n>>> of adding these simpler commands.\n\nDuy is responding to that even if it wasn't included in his quoting.\n\n> >> > +-f::\n> >> > +--force::\n> >> > +       Proceed even if the index or the working tree differs from\n> >> > +       HEAD.  This is used to throw away local changes.\n> >>\n> >> Haven't thought through this thoroughly, but do we really need an\n> >> option for that instead of telling users to 'git reset --hard HEAD'\n> >> before switching branches if they want their stuff thrown away?\n> >\n> > For me it's just a bit more convenient. Hit an error when switching\n> > branch? Recall the command from bash history, stick -f in it and run.\n> > Elsewhere I think both Junio and Thomas (or maybe only Junio) suggests\n> > moving the \"git reset\" functionality without moving HEAD to one of\n> > these commands, which goes the opposite direction...\n>\n> Isn't there a huge difference?  \"checkout --force <other-branch>\"\n> needs to clobber only the changes that are involved in the switch,\n> i.e. if your README.txt is the same between master and maint while\n> Makefile is different, after editing both files while on master, you\n> can not \"switch-branch\" to maint without doing something to Makefile\n> (i.e. either discard your local change or wiggle your local change\n> to the context of 'maint' with \"checkout -m\").  But you can carry\n> the changes to README.txt while checking out 'maint' branch.\n> Running \"git reset --hard HEAD\" would mean that you will lose the\n> changes to README.txt as well.\n\nAh, indeed.  Thanks for pointing out what I missed here.\n\n> >> > +--orphan <new_branch>::\n> >> > +       Create a new 'orphan' branch, named <new_branch>, started from\n> >> > +       <start_point> and switch to it.  The first commit made on this\n> >>\n> >> What??  started from <start_point>?  The whole point of --orphan is\n> >> you have no parent, i.e. no start point.  Also, why does the\n> >> explanation reference an argument that wasn't in the immediately\n> >> preceding synopsis?\n> >\n> > I guess bad phrasing. It should be \"switch to <start_point> first,\n> > then prepare the worktree so that the first commit will have no\n> > parent\". Or something along that line.\n>\n> It should be a <tree-ish>, no?  It is not a \"point\" in history, but\n> is \"start with this tree\".\n\nAre you saying that referring to it as a tree will lessen the\nconfusion users face when they think it's a commit that serves as a\nparent and get confused with the fact that this option is named\n\"--orphan\"?  Or are you making some other orthogonal point here?\n\n> I may have more comments on this message but that's it from me for\n> now.\n\nFair enough.  Sorry if I've distracted from RC stuff with all my responses.\n"},{"id":"364600","messageId":"CABPp-BGZF8s=ReiC=jTKKQbt1LLO72K7a_2pYbQHrw0ZeA9J5w@mail.gmail.com","threadId":"49793","inReplyTo":"xmqqpnugemks.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3 07/14] checkout: split into switch-branch and restore-files","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2018-12-05T04:45:07Z","receivedAt":"2018-12-05T04:45:21Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Dec 4, 2018 at 6:26 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Duy Nguyen <pclouds@gmail.com> writes:\n>\n> > On Tue, Dec 4, 2018 at 6:43 PM Elijah Newren <newren@gmail.com> wrote:\n> >> > > > +--ours::\n> >> > > > +--theirs::\n> >> ...\n> >> go away.  Maybe it can still be fixed (I haven't dug too deeply into\n> >> it), but if so, the only fix needed here would be to remove this long\n> >> explanation about why the tool gets things totally backward.\n> >\n> > Aha. I' not really deep in this merge business to know if stages 2 and\n> > 3 can be swapped. This is right up your alley. I'll just leave it to\n> > you.\n>\n> Please don't show stage#2 and stage#3 swapped to the end user,\n> unless that is protected behind an option (not per-repo config).\n> It is pretty much ingrained that stage#2 is what came from the\n> commit that will become the first parent of the commit being\n> prepared, and changing it without an explicit command line option\n> will break tools.\n\nWhat depends on stage#2 coming from the commit that will become the\nfirst parent?  I wasn't thinking in terms of modifying\ncheckout/restore-files/diff/etc in a way that would make them show\nthings different than what was recorded in the index, I was rather\nmusing on whether it was feasible to have rebase tell the merge\nmachinery to treat HEAD as stage #3 and the other commit as stage #2\nso that it was swapping what was actually recorded in the index.\n\nI know the merge machinery implicitly assumes HEAD == stage #2 in\nmultiple places, and it'd obviously need a fair amount of fixing to\nhandle this.  I wasn't immediately aware of other things that would\nbreak.  If you know of some, I'm happy to hear.  Otherwise, I might go\nand learn the hard way (after I get around to the merge rewrite) why\nmy idea is crazy.  :-)\n\n> > I'm actually still not sure how to move it here (I guess 'here' is\n> > restore-files since we won't move HEAD). All the --mixed, --merge and\n> > --hard are confusing. But maybe we could just make 'git restore-files\n> > --from HEAD -f :/\" behave just like \"git reset --hard HEAD\" (but with\n> > some safety net) But we can leave it for discussion in the next round.\n>\n> Perhaps you two should pay a bit closer attention to what Thomas\n> Gummerer is working on.  I've touched above in my earlier comments,\n> too, e.g.  <xmqqefb3mhrs.fsf@gitster-ct.c.googlers.com>\n\nIndeed, I'm excited about his changes now; I'll keep an eye out.\n"},{"id":"364616","messageId":"xmqqva48bgxi.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"CABPp-BGZF8s=ReiC=jTKKQbt1LLO72K7a_2pYbQHrw0ZeA9J5w@mail.gmail.com","subject":"Re: [PATCH v3 07/14] checkout: split into switch-branch and restore-files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-12-05T06:56:09Z","receivedAt":"2018-12-05T06:56:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> What depends on stage#2 coming from the commit that will become the\n> first parent?\n\nHow about \"git diff --cc\" for a starter?  What came from HEAD's\nancestry should appear first and then what came from the side branch\nthat is merged into.\n\n"},{"id":"367892","messageId":"xmqqimy8a1gx.fsf@gitster-ct.c.googlers.com","threadId":"49793","inReplyTo":"20181113182800.26984-1-pclouds@gmail.com","subject":"Re: [PATCH v2] checkout: print something when checking out paths","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-01-28T21:58:38Z","receivedAt":"2019-01-28T21:58:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> One of the problems with \"git checkout\" is that it does so many\n> different things and could confuse people specially when we fail to\n> handle ambiguation correctly.\n>\n> One way to help with that is tell the user what sort of operation is\n> actually carried out. When switching branches, we always print\n> something unless --quiet, either\n>\n>  - \"HEAD is now at ...\"\n>  - \"Reset branch ...\"\n>  - \"Already on ...\"\n>  - \"Switched to and reset ...\"\n>  - \"Switched to a new branch ...\"\n>  - \"Switched to branch ...\"\n>\n> Checking out paths however is silent. Print something so that if we\n> got the user intention wrong, they won't waste too much time to find\n> that out. For the remaining cases of checkout we now print either\n>\n>  - \"Checked out ... paths out of the index\"\n>  - \"Checked out ... paths out of <abbrev hash>\"\n>\n> Since the purpose of printing this is to help disambiguate. Only do it\n> when \"--\" is missing.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  v2 updates the messages a bit but it does not check isatty or add\n>  --count-paths, for consistency reason with how messages are printed\n>  in the branch switching case.\n>  \n>  Consistency is not always a good reason to follow. But I haven't\n>  seen a strong reason to go against it.\n\nOne small bug I saw since this was merged is that the message that\nis given when unmerging, i.e.\n\n\tgit merge other-branch   ;# conflicts\n\tgit checkout --m <path>\n\nis misleading.  It gives the same \"checked out ... out of the index\",\nbut it should be made a lot more distinct, perhaps \"unmerged N paths\".\n"},{"id":"367910","messageId":"CACsJy8B9W3XcD4ucAt7roA9grJppYK+uTH35xvafaTbkMNPAGw@mail.gmail.com","threadId":"49793","inReplyTo":"xmqqimy8a1gx.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2] checkout: print something when checking out paths","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-01-29T01:26:07Z","receivedAt":"2019-01-29T01:26:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Jan 29, 2019 at 4:58 AM Junio C Hamano <gitster@pobox.com> wrote:\n> One small bug I saw since this was merged is that the message that\n> is given when unmerging, i.e.\n>\n>         git merge other-branch   ;# conflicts\n>         git checkout --m <path>\n>\n> is misleading.  It gives the same \"checked out ... out of the index\",\n> but it should be made a lot more distinct, perhaps \"unmerged N paths\".\n\nRight, I missed this. How about \"re-created %d merge conflicts\"? It's\na bit clearer, I think.\n-- \nDuy\n"},{"id":"368563","messageId":"20190206025115.26163-1-pclouds@gmail.com","threadId":"49793","inReplyTo":"xmqqimy8a1gx.fsf@gitster-ct.c.googlers.com","subject":"[PATCH 0/2] nd/checkout-noisy updates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-06T02:51:13Z","receivedAt":"2019-02-06T02:51:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This fixes the misleading message from \"git checkout -m\". I also\nrephrased the original messages a bit so that when\ntg/checkout-no-overlay is merged, fixing counting file deletion is one\nline change.\n\nNguyễn Thái Ngọc Duy (2):\n  checkout: update count-checkouts messages\n  checkout: count and print -m paths separately\n\n builtin/checkout.c | 19 ++++++++++++-------\n 1 file changed, 12 insertions(+), 7 deletions(-)\n\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368564","messageId":"20190206025115.26163-2-pclouds@gmail.com","threadId":"49793","inReplyTo":"20190206025115.26163-1-pclouds@gmail.com","subject":"[PATCH 1/2] checkout: update count-checkouts messages","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-06T02:51:14Z","receivedAt":"2019-02-06T02:51:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Commit 0f086e6dca [1] counts the number of files updated by \"git\ncheckout -- <paths>\" command and prints it. Later on 536ec1839d [2]\nadds the ability to remove files in \"git checkout -- <paths>\". This is\nstill an update on worktree and should be reported to the user.\n\nTo prepare for such an update since that commit is on track to\n'master' now, the messages are rephrased to avoid \"checked out\" which\ndoes not imply file deletion.\n\n[1] 0f086e6dca (checkout: print something when checking out paths -\n    2018-11-13)\n[2] 536ec1839d (entry: support CE_WT_REMOVE flag in checkout_entry -\n    2018-12-20)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 9f8f3466f6..6e33850d9f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -393,15 +393,15 @@ static int checkout_paths(const struct checkout_opts *opts,\n \n \tif (opts->count_checkout_paths) {\n \t\tif (opts->source_tree)\n-\t\t\tfprintf_ln(stderr, Q_(\"Checked out %d path out of %s\",\n-\t\t\t\t\t      \"Checked out %d paths out of %s\",\n+\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from %s\",\n+\t\t\t\t\t      \"Updated %d paths from %s\",\n \t\t\t\t\t      nr_checkouts),\n \t\t\t\t   nr_checkouts,\n \t\t\t\t   find_unique_abbrev(&opts->source_tree->object.oid,\n \t\t\t\t\t\t      DEFAULT_ABBREV));\n \t\telse\n-\t\t\tfprintf_ln(stderr, Q_(\"Checked out %d path out of the index\",\n-\t\t\t\t\t      \"Checked out %d paths out of the index\",\n+\t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from the index\",\n+\t\t\t\t\t      \"Updated %d paths from the index\",\n \t\t\t\t\t      nr_checkouts),\n \t\t\t\t   nr_checkouts);\n \t}\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"368565","messageId":"20190206025115.26163-3-pclouds@gmail.com","threadId":"49793","inReplyTo":"20190206025115.26163-1-pclouds@gmail.com","subject":"[PATCH 2/2] checkout: count and print -m paths separately","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2019-02-06T02:51:15Z","receivedAt":"2019-02-06T02:51:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Since 0f086e6dca (checkout: print something when checking out paths -\n2018-11-13), this command reports how many paths have been updated\nfrom what source (either from a tree, or from the index). I forget\nthat there's a third source: when -m is used, the merge conflict is\nre-created (granted, also from the index, but it's not a straight copy\nfrom the index).\n\nCount and report unmerged paths separately. There's a bit more update\nto avoid reporting:\n\n   Recreated X merge conflicts\n   Updated 0 paths from the index\n\nThe second line is unnecessary. Though if there's no conflict\nrecreation, we still report\n\n   Updated 0 paths from the index\n\nto make it clear we're not really doing anything.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 11 ++++++++---\n 1 file changed, 8 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 6e33850d9f..e762b53542 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -260,7 +260,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \tstruct commit *head;\n \tint errs = 0;\n \tstruct lock_file lock_file = LOCK_INIT;\n-\tint nr_checkouts = 0;\n+\tint nr_checkouts = 0, nr_unmerged = 0;\n \n \tif (opts->track != BRANCH_TRACK_UNSPECIFIED)\n \t\tdie(_(\"'%s' cannot be used with updating paths\"), \"--track\");\n@@ -385,13 +385,18 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\t\t\t\t\t       &state, &nr_checkouts);\n \t\t\telse if (opts->merge)\n \t\t\t\terrs |= checkout_merged(pos, &state,\n-\t\t\t\t\t\t\t&nr_checkouts);\n+\t\t\t\t\t\t\t&nr_unmerged);\n \t\t\tpos = skip_same_name(ce, pos) - 1;\n \t\t}\n \t}\n \terrs |= finish_delayed_checkout(&state, &nr_checkouts);\n \n \tif (opts->count_checkout_paths) {\n+\t\tif (nr_unmerged)\n+\t\t\tfprintf_ln(stderr, Q_(\"Recreated %d merge conflict\",\n+\t\t\t\t\t      \"Recreated %d merge conflicts\",\n+\t\t\t\t\t      nr_unmerged),\n+\t\t\t\t   nr_unmerged);\n \t\tif (opts->source_tree)\n \t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from %s\",\n \t\t\t\t\t      \"Updated %d paths from %s\",\n@@ -399,7 +404,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\t\t\t   nr_checkouts,\n \t\t\t\t   find_unique_abbrev(&opts->source_tree->object.oid,\n \t\t\t\t\t\t      DEFAULT_ABBREV));\n-\t\telse\n+\t\telse if (!nr_unmerged || nr_checkouts)\n \t\t\tfprintf_ln(stderr, Q_(\"Updated %d path from the index\",\n \t\t\t\t\t      \"Updated %d paths from the index\",\n \t\t\t\t\t      nr_checkouts),\n-- \n2.20.1.682.gd5861c6d90\n\n"},{"id":"371085","messageId":"20190310193211.GA444@esm","threadId":"49793","inReplyTo":"20181129215850.7278-12-pclouds@gmail.com","subject":"Re: [PATCH v3 11/14] switch-branch: only allow explicit detached HEAD","fromName":"Eckhard Maaß","fromEmail":"eckhard.s.maass@googlemail.com","sentAt":"2019-03-10T19:32:11Z","receivedAt":"2019-03-10T19:32:17Z","isPatch":true,"sender":{"key":"eckhard.s.maass@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/21984134?v=4"},"body":"On Thu, Nov 29, 2018 at 10:58:46PM +0100, Nguyễn Thái Ngọc Duy wrote:\n> +\tif (!opts->implicit_detach &&\n> +\t    !opts->new_branch &&\n> +\t    !opts->new_branch_force &&\n> +\t    new_branch_info->name &&\n> +\t    !new_branch_info->path)\n> +\t\tdie(_(\"a branch is expected, got %s\"), new_branch_info->name);\n\nWouldn't it be nice to give more context here, for example the symbolic\nreference that the name actually points to? When expereimenting with the\nfeature and trying to switch to a tag, it refuses with\n\"a branch is expected, got v1.2.0\". I personally would prefer something\nmore like \"a branch is expected, got v1.2.0 that resolved to\nrefs/tags/v1.2.0\", so I get \"oh, yeah, that is actually a tag ...\". Does\nthis seem worthwhile to dig deeper into? A quick glance left me a bit\npuzzled, I admit.\n\nGreetings,\nEckhard\n"},{"id":"371150","messageId":"CACsJy8BeEHv8uhgi-2J++-t2mktQprGNTPK9kF+7af1tmhZ29w@mail.gmail.com","threadId":"49793","inReplyTo":"20190310193211.GA444@esm","subject":"Re: [PATCH v3 11/14] switch-branch: only allow explicit detached HEAD","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-03-11T14:27:18Z","receivedAt":"2019-03-11T14:27:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 11, 2019 at 2:32 AM Eckhard Maaß\n<eckhard.s.maass@googlemail.com> wrote:\n>\n> On Thu, Nov 29, 2018 at 10:58:46PM +0100, Nguyễn Thái Ngọc Duy wrote:\n> > +     if (!opts->implicit_detach &&\n> > +         !opts->new_branch &&\n> > +         !opts->new_branch_force &&\n> > +         new_branch_info->name &&\n> > +         !new_branch_info->path)\n> > +             die(_(\"a branch is expected, got %s\"), new_branch_info->name);\n>\n> Wouldn't it be nice to give more context here, for example the symbolic\n> reference that the name actually points to? When expereimenting with the\n> feature and trying to switch to a tag, it refuses with\n> \"a branch is expected, got v1.2.0\". I personally would prefer something\n> more like \"a branch is expected, got v1.2.0 that resolved to\n> refs/tags/v1.2.0\", so I get \"oh, yeah, that is actually a tag ...\". Does\n> this seem worthwhile to dig deeper into? A quick glance left me a bit\n> puzzled, I admit.\n\nGood suggestion. I'll try to report one of the following\n\na branch is expected, got tag 'v1.2.0'\na branch is expected, got remote branch 'origin/master'\na branch is expected, got 'refs/foo/bar'\na branch is expected, got commit 'HEAD^'\n\nIt's a bit more code, but I think it definitely helps.\n-- \nDuy\n"}]}