{"thread":{"id":"24156","subject":"[PATCH 1/3] add tests for checkout -b","startedAt":"2010-06-20T16:33:09Z","lastAt":"2010-06-23T21:37:02Z","messageCount":29,"participants":["Tay Ray Chuan","Junio C Hamano","Jeff King","Clemens Buchacher","Michel Lespinasse","Erick Mattos"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"143956","messageId":"1277051592-4552-1-git-send-email-rctay89@gmail.com","threadId":"24156","inReplyTo":null,"subject":"[PATCH 0/3] DWIM with \"git checkout -f -b\"","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-20T16:33:09Z","receivedAt":"2010-06-20T16:33:09Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Right now, the -f/--force in git-checkout doesn't carry over to -b\noperations - ie. you can't do git checkout -b branch on an existing\nbranch. This patch series changes this, so that one can do\n\n  git checkout -f -b branch ref\n\nwhich is equivalent to\n\n  git branch -f branch ref && git checkout branch\n\nContents:\n[PATCH 1/3] add tests for checkout -b\n[PATCH 2/3] t2018-checkout-branch.sh: show that checkout -f -b doesn't DWIM\n[PATCH 3/3] builtin/checkout: DWIM with -f -b\n\n Documentation/git-checkout.txt |    3 +\n builtin/checkout.c             |    4 +-\n t/t2018-checkout-branch.sh     |   86 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 91 insertions(+), 2 deletions(-)\n create mode 100755 t/t2018-checkout-branch.sh\n"},{"id":"143955","messageId":"1277051592-4552-2-git-send-email-rctay89@gmail.com","threadId":"24156","inReplyTo":"1277051592-4552-1-git-send-email-rctay89@gmail.com","subject":"[PATCH 1/3] add tests for checkout -b","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-20T16:33:10Z","receivedAt":"2010-06-20T16:33:10Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n t/t2018-checkout-branch.sh |   66 ++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 66 insertions(+), 0 deletions(-)\n create mode 100755 t/t2018-checkout-branch.sh\n\ndiff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh\nnew file mode 100755\nindex 0000000..678a34f\n--- /dev/null\n+++ b/t/t2018-checkout-branch.sh\n@@ -0,0 +1,66 @@\n+#!/bin/sh\n+\n+test_description='checkout '\n+\n+. ./test-lib.sh\n+\n+# Arguments: <branch> <sha> [<checkout options>]\n+do_checkout() {\n+\texp_branch=$1 &&\n+\texp_ref=\"refs/heads/$exp_branch\" &&\n+\n+\t# if <sha> is not specified, use HEAD.\n+\texp_sha=${2:-$(git rev-parse --verify HEAD)} &&\n+\n+\tgit checkout ${3+\"$3\"} -b $exp_branch $exp_sha &&\n+\n+\ttest $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&\n+\ttest $exp_sha = $(git rev-parse --verify HEAD)\n+}\n+\n+test_dirty() {\n+\t! git diff --exit-code >/dev/null\n+}\n+\n+setup_dirty() {\n+\techo >>file1 change2\n+}\n+\n+test_expect_success 'setup' '\n+\ttest_commit initial file1 &&\n+\tHEAD1=$(git rev-parse --verify HEAD) &&\n+\n+\ttest_commit change1 file1 &&\n+\tHEAD2=$(git rev-parse --verify HEAD) &&\n+\n+\tgit branch -m branch1\n+'\n+\n+test_expect_success 'checkout -b to a new branch' '\n+\tdo_checkout branch2\n+'\n+\n+test_expect_success 'checkout -b to a new branch (explicit ref)' '\n+\tgit checkout branch1 &&\n+\tgit branch -D branch2 &&\n+\n+\tdo_checkout branch2 $HEAD1\n+'\n+\n+test_expect_success 'checkout -b to a new branch (dirty)' '\n+\tgit checkout branch1 &&\n+\tgit branch -D branch2 &&\n+\n+\tsetup_dirty &&\n+\ttest_must_fail do_checkout branch2 $HEAD1 &&\n+\ttest_dirty\n+'\n+\n+test_expect_success 'checkout -b to an existing branch fails' '\n+\tgit reset --hard HEAD &&\n+\tgit branch branch2 &&\n+\n+\ttest_must_fail do_checkout branch2 $HEAD2\n+'\n+\n+test_done\n-- \n1.7.1.513.g4f18\n"},{"id":"143958","messageId":"1277051592-4552-3-git-send-email-rctay89@gmail.com","threadId":"24156","inReplyTo":"1277051592-4552-2-git-send-email-rctay89@gmail.com","subject":"[PATCH 2/3] t2018-checkout-branch.sh: show that checkout -f -b doesn't DWIM","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-20T16:33:11Z","receivedAt":"2010-06-20T16:33:11Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n t/t2018-checkout-branch.sh |   20 ++++++++++++++++++++\n 1 files changed, 20 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh\nindex 678a34f..e6c0f8c 100755\n--- a/t/t2018-checkout-branch.sh\n+++ b/t/t2018-checkout-branch.sh\n@@ -63,4 +63,24 @@ test_expect_success 'checkout -b to an existing branch fails' '\n \ttest_must_fail do_checkout branch2 $HEAD2\n '\n \n+test_expect_failure 'checkout -f -b to an existing branch resets branch' '\n+\tgit checkout branch1 &&\n+\n+\tdo_checkout branch2 \"\" -f\n+'\n+\n+test_expect_failure 'checkout -f -b to an existing branch resets branch (explicit ref)' '\n+\tgit checkout branch1 &&\n+\n+\tdo_checkout branch2 $HEAD1 -f\n+'\n+\n+test_expect_failure 'checkout -f -b to an existing branch resets branch (dirty) ' '\n+\tgit checkout branch1 &&\n+\n+\tsetup_dirty &&\n+\tdo_checkout branch2 $HEAD1 -f &&\n+\ttest_must_fail test_dirty\n+'\n+\n test_done\n-- \n1.7.1.513.g4f18\n"},{"id":"143957","messageId":"1277051592-4552-4-git-send-email-rctay89@gmail.com","threadId":"24156","inReplyTo":"1277051592-4552-3-git-send-email-rctay89@gmail.com","subject":"[PATCH 3/3] builtin/checkout: DWIM with -f -b","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-20T16:33:12Z","receivedAt":"2010-06-20T16:33:12Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n Documentation/git-checkout.txt |    3 +++\n builtin/checkout.c             |    4 ++--\n t/t2018-checkout-branch.sh     |    6 +++---\n 3 files changed, 8 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 261dd90..d15d224 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -74,6 +74,9 @@ entries; instead, unmerged entries are ignored.\n -b::\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+When used with `-f`, the branch is reset; changes in the index or the\n+working tree are thrown away too.\n \n -t::\n --track::\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1994be9..962d938 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -511,7 +511,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t}\n \t\t}\n \t\telse\n-\t\t\tcreate_branch(old->name, opts->new_branch, new->name, 0,\n+\t\t\tcreate_branch(old->name, opts->new_branch, new->name, opts->force,\n \t\t\t\t      opts->new_branch_log, opts->track);\n \t\tnew->name = opts->new_branch;\n \t\tsetup_branch_path(new);\n@@ -858,7 +858,7 @@ no_reference:\n \t\tif (strbuf_check_branch_ref(&buf, opts.new_branch))\n \t\t\tdie(\"git checkout: we do not like '%s' as a branch name.\",\n \t\t\t    opts.new_branch);\n-\t\tif (!get_sha1(buf.buf, rev))\n+\t\tif (!get_sha1(buf.buf, rev) && !opts.force)\n \t\t\tdie(\"git checkout: branch %s already exists\", opts.new_branch);\n \t\tstrbuf_release(&buf);\n \t}\ndiff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh\nindex e6c0f8c..9895ff5 100755\n--- a/t/t2018-checkout-branch.sh\n+++ b/t/t2018-checkout-branch.sh\n@@ -63,19 +63,19 @@ test_expect_success 'checkout -b to an existing branch fails' '\n \ttest_must_fail do_checkout branch2 $HEAD2\n '\n \n-test_expect_failure 'checkout -f -b to an existing branch resets branch' '\n+test_expect_success 'checkout -f -b to an existing branch resets branch' '\n \tgit checkout branch1 &&\n \n \tdo_checkout branch2 \"\" -f\n '\n \n-test_expect_failure 'checkout -f -b to an existing branch resets branch (explicit ref)' '\n+test_expect_success 'checkout -f -b to an existing branch resets branch (explicit ref)' '\n \tgit checkout branch1 &&\n \n \tdo_checkout branch2 $HEAD1 -f\n '\n \n-test_expect_failure 'checkout -f -b to an existing branch resets branch (dirty) ' '\n+test_expect_success 'checkout -f -b to an existing branch resets branch (dirty) ' '\n \tgit checkout branch1 &&\n \n \tsetup_dirty &&\n-- \n1.7.1.513.g4f18\n"},{"id":"143961","messageId":"7vvd9dtt54.fsf@alter.siamese.dyndns.org","threadId":"24156","inReplyTo":"1277051592-4552-4-git-send-email-rctay89@gmail.com","subject":"Re: [PATCH 3/3] builtin/checkout: DWIM with -f -b","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-06-20T19:12:55Z","receivedAt":"2010-06-20T19:12:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I think this is not \"DWIM\" but is \"-f does not work with -b; fix it\".\n\nWill queue; thanks.\n"},{"id":"143962","messageId":"20100620201137.GA8502@coredump.intra.peff.net","threadId":"24156","inReplyTo":"7vvd9dtt54.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 3/3] builtin/checkout: DWIM with -f -b","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-06-20T20:11:38Z","receivedAt":"2010-06-20T20:11:38Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Jun 20, 2010 at 12:12:55PM -0700, Junio C Hamano wrote:\n\n> I think this is not \"DWIM\" but is \"-f does not work with -b; fix it\".\n> \n> Will queue; thanks.\n\nI am not sure it is fixing a bug. There are really two possible things\nto be forced during checkout:\n\n  1. throw away local changes\n\n  2. overwrite an existing branch\n\nRight now, \"checkout -f\" means just (1). This patch makes it _also_ mean\n(2). Do we want to conflate those two cases?\n\nIn some sense, it is reasonable for \"-f\" to mean \"force anything that\nmight be questionable\". But I wonder if it pays to be a little more\nconservative given that this is a safety valve we are talking about. The\nuser can of course still split their compound action into:\n\n  git branch [-f] new_branch old_branch\n  git checkout [-f] new_branch\n\nLess convenient, but there is no ambiguity.\n\nI dunno. I don't have a strong feeling on how it should be, but I think\nit is more than a simple bug fix.\n\n-Peff\n"},{"id":"143963","messageId":"20100620210722.GA27492@localhost","threadId":"24156","inReplyTo":"20100620201137.GA8502@coredump.intra.peff.net","subject":"Re: [PATCH 3/3] builtin/checkout: DWIM with -f -b","fromName":"Clemens Buchacher","fromEmail":"drizzd@aon.at","sentAt":"2010-06-20T21:07:23Z","receivedAt":"2010-06-20T21:07:23Z","isPatch":true,"sender":{"key":"drizzd@gmx.net","avatar":"https://avatars.githubusercontent.com/u/59082?v=4"},"body":"On Sun, Jun 20, 2010 at 04:11:38PM -0400, Jeff King wrote:\n> On Sun, Jun 20, 2010 at 12:12:55PM -0700, Junio C Hamano wrote:\n> \n> > I think this is not \"DWIM\" but is \"-f does not work with -b; fix it\".\n\nActually, it currently behaves exactly as the manpage suggests.\n\n> I am not sure it is fixing a bug. There are really two possible things\n> to be forced during checkout:\n> \n>   1. throw away local changes\n> \n>   2. overwrite an existing branch\n> \n> Right now, \"checkout -f\" means just (1). This patch makes it _also_ mean\n> (2). Do we want to conflate those two cases?\n> \n[...]\n> I dunno. I don't have a strong feeling on how it should be, but I think\n> it is more than a simple bug fix.\n\nI don't have anything to add to that, except that in my opinion\ncheckout does more than enough conflating already, and I am\ntherefore strongly against this change.\n\nClemens\n"},{"id":"143964","messageId":"7vpqzltnp3.fsf@alter.siamese.dyndns.org","threadId":"24156","inReplyTo":"20100620201137.GA8502@coredump.intra.peff.net","subject":"Re: [PATCH 3/3] builtin/checkout: DWIM with -f -b","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-06-20T21:10:32Z","receivedAt":"2010-06-20T21:10:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> I am not sure it is fixing a bug. There are really two possible things\n> to be forced during checkout:\n>\n>   1. throw away local changes\n>\n>   2. overwrite an existing branch\n>\n> Right now, \"checkout -f\" means just (1). This patch makes it _also_ mean\n> (2). Do we want to conflate those two cases?\n>\n> In some sense, it is reasonable for \"-f\" to mean \"force anything that\n> might be questionable\". But I wonder if it pays to be a little more\n> conservative given that this is a safety valve we are talking about. The\n> user can of course still split their compound action into:\n>\n>   git branch [-f] new_branch old_branch\n>   git checkout [-f] new_branch\n>\n> Less convenient, but there is no ambiguity.\n>\n> I dunno. I don't have a strong feeling on how it should be, but I think\n> it is more than a simple bug fix.\n\nOk, fair enough.\n\nFWIW, I often end up doing this when rebuilding 'pu' (and 'jch' that is\nused internally that keeps a version of 'next' with cleaner history).\n\n    ... a script to regenerate jch on top of master (and pu on jch)\n    $ Meta/Reintegrate master..jch >Meta/redo-jch.sh\n    $ Meta/Reintegrate jch..pu >Meta/redo-pu.sh\n\n    ... rewind and rebuild jch on top of updated master\n    $ git checkout -f -b jch master\n    ... error ... oops\n    $ git branch -f jch master\n    $ git checkout jch\n    $ Meta/redo-jch.sh\n    $ git diff next\n    ... no output---good.\n\n    $ git checkout -f -b pu jch\n    ... error ... oops\n    $ git branch -f pu jch\n    $ git checkout pu\n    $ Meta/redo-pu.sh\n"},{"id":"143965","messageId":"20100620211605.GA2559@sigill.intra.peff.net","threadId":"24156","inReplyTo":"7vpqzltnp3.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 3/3] builtin/checkout: DWIM with -f -b","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-06-20T21:16:05Z","receivedAt":"2010-06-20T21:16:05Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Jun 20, 2010 at 02:10:32PM -0700, Junio C Hamano wrote:\n\n> FWIW, I often end up doing this when rebuilding 'pu' (and 'jch' that is\n> used internally that keeps a version of 'next' with cleaner history).\n> \n>     ... a script to regenerate jch on top of master (and pu on jch)\n>     $ Meta/Reintegrate master..jch >Meta/redo-jch.sh\n>     $ Meta/Reintegrate jch..pu >Meta/redo-pu.sh\n> \n>     ... rewind and rebuild jch on top of updated master\n>     $ git checkout -f -b jch master\n>     ... error ... oops\n>     $ git branch -f jch master\n>     $ git checkout jch\n>     $ Meta/redo-jch.sh\n>     $ git diff next\n>     ... no output---good.\n\nI'm certainly sympathetic, but as I understand it, with this new patch\nyou are doing the equivalent of:\n\n  git branch -f jch master\n  git checkout -f jch\n\nwhich is different than what you wrote above.  For your workflow, I\ndoubt it matters, but it is potentially destructive.\n\n-Peff\n"},{"id":"143968","messageId":"7vfx0htfer.fsf@alter.siamese.dyndns.org","threadId":"24156","inReplyTo":"20100620211605.GA2559@sigill.intra.peff.net","subject":"Re: [PATCH 3/3] builtin/checkout: DWIM with -f -b","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-06-21T00:09:32Z","receivedAt":"2010-06-21T00:09:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> ...  For your workflow, I\n> doubt it matters, but it is potentially destructive.\n\nYes; I thought the implication of \"-f\" to be destructive would be a\njustification enough, but I agree with you that conflating the two may be\na bad idea.  When a user says \"git checkout -f -b jch\" after seeing the\ncommand without \"-f\" fail due to existing \"jch\", it is quite clear that\nthe user wants to clobber the history of existing \"jch\" branch (why else\nwould he giving \"-f\"), but it is not a justification to clobber local\nchanges he has in the index and the work tree.\n"},{"id":"143969","messageId":"AANLkTik3QZBpOjwZHGwIXphXnaHLaBV4pUSb1OVAau9V@mail.gmail.com","threadId":"24156","inReplyTo":"7vfx0htfer.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 3/3] builtin/checkout: DWIM with -f -b","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-21T01:08:27Z","receivedAt":"2010-06-21T01:08:27Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Hi,\n\nOn Mon, Jun 21, 2010 at 8:09 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jeff King <peff@peff.net> writes:\n>\n>> ...  For your workflow, I\n>> doubt it matters, but it is potentially destructive.\n>\n> Yes; I thought the implication of \"-f\" to be destructive would be a\n> justification enough, but I agree with you that conflating the two may be\n> a bad idea.  When a user says \"git checkout -f -b jch\" after seeing the\n> command without \"-f\" fail due to existing \"jch\", it is quite clear that\n> the user wants to clobber the history of existing \"jch\" branch (why else\n> would he giving \"-f\"), but it is not a justification to clobber local\n> changes he has in the index and the work tree.\n\nHow about doing\n\n  git checkout -f -f -b <branch>\n\n?\n\nBy having the user to specify \"-f\" twice, we're can be really sure\nthat the user wants to\n\n  1) throw away local changes, which is what the current \"-f\" is\nsupposed to do, and\n  2) reset an existing branch - new behaviour.\n\n\n-- \nCheers,\nRay Chuan\n"},{"id":"143970","messageId":"AANLkTildfbJvt15cObc52LvOQRvasL4WYsJq3126bBGd@mail.gmail.com","threadId":"24156","inReplyTo":"20100620211605.GA2559@sigill.intra.peff.net","subject":"Re: [PATCH 3/3] builtin/checkout: DWIM with -f -b","fromName":"Michel Lespinasse","fromEmail":"walken@google.com","sentAt":"2010-06-21T02:58:53Z","receivedAt":"2010-06-21T02:58:53Z","isPatch":true,"sender":{"key":"walken@google.com","avatar":null},"body":"On Sun, Jun 20, 2010 at 2:16 PM, Jeff King <peff@peff.net> wrote:\n> I'm certainly sympathetic, but as I understand it, with this new patch\n> you are doing the equivalent of:\n>\n>  git branch -f jch master\n>  git checkout -f jch\n>\n> which is different than what you wrote above.  For your workflow, I\n> doubt it matters, but it is potentially destructive.\n\nI am frequently trying to do this as well, so I would love the\ncheckout -f -b option.\n\nHowever, I think that the desired behavior for checkout -f -b is that\nit should be equivalent to:\ngit branch -f jch master\ngit checkout jch   (without the -f here)\n\nrationale: git checkout -b normally preserves the index & the working\ntree; adding the -f flag to a command that would otherwise succeed\nshould not change its behavior.\n\n-- \nMichel \"Walken\" Lespinasse\nA program is never fully debugged until the last user dies.\n"},{"id":"143971","messageId":"AANLkTikvTtmz_uai5SNO0Co8PWLgK7rIdl7cCqHosViY@mail.gmail.com","threadId":"24156","inReplyTo":"AANLkTildfbJvt15cObc52LvOQRvasL4WYsJq3126bBGd@mail.gmail.com","subject":"Re: [PATCH 3/3] builtin/checkout: DWIM with -f -b","fromName":"Michel Lespinasse","fromEmail":"walken@google.com","sentAt":"2010-06-21T04:09:35Z","receivedAt":"2010-06-21T04:09:35Z","isPatch":true,"sender":{"key":"walken@google.com","avatar":null},"body":"On Sun, Jun 20, 2010 at 7:58 PM, Michel Lespinasse <walken@google.com> wrote:\n> On Sun, Jun 20, 2010 at 2:16 PM, Jeff King <peff@peff.net> wrote:\n>> I'm certainly sympathetic, but as I understand it, with this new patch\n>> you are doing the equivalent of:\n>>\n>>  git branch -f jch master\n>>  git checkout -f jch\n>>\n>> which is different than what you wrote above.  For your workflow, I\n>> doubt it matters, but it is potentially destructive.\n>\n> I am frequently trying to do this as well, so I would love the\n> checkout -f -b option.\n>\n> However, I think that the desired behavior for checkout -f -b is that\n> it should be equivalent to:\n> git branch -f jch master\n> git checkout jch   (without the -f here)\n\nRhaaa, that actually wouldn't work as the git checkout command would\nfail if there are changes in the index & working tree. What I want\nhere is actually for git checkout -f -b jch to be equivalent to:\n\ngit branch -D jch  (ignore error if jch branch does not exist)\ngit checkout -b jch\n\n> rationale: git checkout -b normally preserves the index & the working\n> tree; adding the -f flag to a command that would otherwise succeed\n> should not change its behavior.\n\nAt least the rationale still holds :)\n\n-- \nMichel \"Walken\" Lespinasse\nA program is never fully debugged until the last user dies.\n"},{"id":"143974","messageId":"7vpqzlrmo4.fsf@alter.siamese.dyndns.org","threadId":"24156","inReplyTo":"AANLkTik3QZBpOjwZHGwIXphXnaHLaBV4pUSb1OVAau9V@mail.gmail.com","subject":"Re: [PATCH 3/3] builtin/checkout: DWIM with -f -b","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-06-21T05:15:39Z","receivedAt":"2010-06-21T05:15:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Tay Ray Chuan <rctay89@gmail.com> writes:\n\n>> Yes; I thought the implication of \"-f\" to be destructive would be a\n>> justification enough, but I agree with you that conflating the two may be\n>> a bad idea.  When a user says \"git checkout -f -b jch\" after seeing the\n>> command without \"-f\" fail due to existing \"jch\", it is quite clear that\n>> the user wants to clobber the history of existing \"jch\" branch (why else\n>> would he giving \"-f\"), but it is not a justification to clobber local\n>> changes he has in the index and the work tree.\n>\n> How about doing\n>\n>   git checkout -f -f -b <branch>\n\nThat is not the problem I want you to solve.  What your patch does solves\nonly half of my issue as Peff pointed out (it lets me reuse the name of\nthe existing branch for an unrelated history, but it loses the local\nchanges I may have in my working tree).  IOW, I want to start <branch>\nfrom scratch, based on an half-done work I started from my current branch\nin my work tree.\n\nThat is what \"git checkout -b <branch>\" usually gives us, if the name\n<branch> is not in use.\n\nI think you were suggesting not to nuke local change with a single -f\nonly when -b is also in use, but I think that makes things even more\nconfusing to the users.  Sometimes a single -f discards local changes, but\nsome other times you would need double -f.  That will lead to insanity.\n\nPerhaps we would want another option that is similar to -b but lets us\ndiscard the named branch if it exists.  Let's call it tentatively -B.\n\n * git checkout -b <branch> [<start-point>]\n\n   - starts a new branch <branch> at <start-point> commit;\n\n   - attempts three-way merge to keep the local change forward while doing\n     so, but aborts if it needs a real file-level merge;\n\n   - complains and aborts if <branch> already exists.\n\n   When -f is used, instead of attempting three-way merge to keep the local\n   changes, it discards them.\n\n   When -m is used, instead of failing when it needs a real file-level merge,\n   it will attempt CVS/SVN \"update\" style merge (and can cause conflicts).\n\n   When -B is used instead of -b, it does the three-way merge and the usual\n   rejection upon a file-level merge unless -m is given.  If it can go ahead,\n   but <branch> already exists, then the existing <branch> is discarded and\n   replaced with the <start-point> commit.\n\nSomething like that would reduce the confusion factor and would help the\n\"recreate an existing branch from scratch\" (or \"reuse the name of an\nexisting branch\") workflow a bit.\n"},{"id":"143979","messageId":"1277140782-4064-1-git-send-email-rctay89@gmail.com","threadId":"24156","inReplyTo":"7vpqzlrmo4.fsf@alter.siamese.dyndns.org","subject":"[PATCH 0/3] teach git-checkout -B WAS builtin/checkout: DWIM with -f -b","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-21T17:19:39Z","receivedAt":"2010-06-21T17:19:39Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Right now, the -f/--force in git-checkout doesn't carry over to -b\noperations - ie. you can't do git checkout -b branch on an existing\nbranch.\n\nThis patch allows you to do this with the -B option, so that one can do\n\n  git checkout -B branch ref\n\nwhich is equivalent to\n\n  git branch -f branch ref && git checkout branch\n\nContents:\n[PATCH 1/3] add tests for checkout -b\n[PATCH 2/3] builtin/checkout: change -b from an OPTION_STRING to a OPTION_SET_INT\n[PATCH 3/3] builtin/checkout: learn -B\n\nTay Ray Chuan (3):\n  add tests for checkout -b\n  builtin/checkout: change -b from an OPTION_STRING to a OPTION_SET_INT\n  builtin/checkout: learn -B\n\n Documentation/git-checkout.txt |   15 +++-\n builtin/checkout.c             |   46 +++++++----\n t/t2018-checkout-branch.sh     |  166 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 209 insertions(+), 18 deletions(-)\n create mode 100755 t/t2018-checkout-branch.sh\n"},{"id":"143980","messageId":"1277140782-4064-2-git-send-email-rctay89@gmail.com","threadId":"24156","inReplyTo":"1277140782-4064-1-git-send-email-rctay89@gmail.com","subject":"[PATCH 1/3] add tests for checkout -b","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-21T17:19:40Z","receivedAt":"2010-06-21T17:19:40Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n t/t2018-checkout-branch.sh |  121 ++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 121 insertions(+), 0 deletions(-)\n create mode 100755 t/t2018-checkout-branch.sh\n\ndiff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh\nnew file mode 100755\nindex 0000000..3c13065\n--- /dev/null\n+++ b/t/t2018-checkout-branch.sh\n@@ -0,0 +1,121 @@\n+#!/bin/sh\n+\n+test_description='checkout '\n+\n+. ./test-lib.sh\n+\n+# Arguments: <branch> <sha> [<checkout options>]\n+#\n+# Runs \"git checkout\" to switch to <branch>, testing that\n+#\n+#   1) we are on the specified branch, <branch>;\n+#   2) HEAD is <sha>; if <sha> is not specified, the old HEAD is used.\n+#\n+# If <checkout options> is not specified, \"git checkout\" is run with -b.\n+do_checkout() {\n+\texp_branch=$1 &&\n+\texp_ref=\"refs/heads/$exp_branch\" &&\n+\n+\t# if <sha> is not specified, use HEAD.\n+\texp_sha=${2:-$(git rev-parse --verify HEAD)} &&\n+\n+\t# default options for git checkout: -b\n+\tif [ -z \"$3\" ]; then\n+\t\topts=\"-b\"\n+\telse\n+\t\topts=\"$3\"\n+\tfi\n+\n+\tgit checkout $opts $exp_branch $exp_sha &&\n+\n+\ttest $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&\n+\ttest $exp_sha = $(git rev-parse --verify HEAD)\n+}\n+\n+test_dirty_unmergeable() {\n+\t! git diff --exit-code >/dev/null\n+}\n+\n+setup_dirty_unmergeable() {\n+\techo >>file1 change2\n+}\n+\n+test_dirty_mergeable() {\n+\t! git diff --cached --exit-code >/dev/null\n+}\n+\n+setup_dirty_mergeable() {\n+\techo >file2 file2 &&\n+\tgit add file2\n+}\n+\n+test_expect_success 'setup' '\n+\ttest_commit initial file1 &&\n+\tHEAD1=$(git rev-parse --verify HEAD) &&\n+\n+\ttest_commit change1 file1 &&\n+\tHEAD2=$(git rev-parse --verify HEAD) &&\n+\n+\tgit branch -m branch1\n+'\n+\n+test_expect_success 'checkout -b to a new branch, set to HEAD' '\n+\tdo_checkout branch2\n+'\n+\n+test_expect_success 'checkout -b to a new branch, set to an explicit ref' '\n+\tgit checkout branch1 &&\n+\tgit branch -D branch2 &&\n+\n+\tdo_checkout branch2 $HEAD1\n+'\n+\n+test_expect_success 'checkout -b to a new branch with unmergeable changes fails' '\n+\tgit checkout branch1 &&\n+\n+\t# clean up from previous test\n+\tgit branch -D branch2 &&\n+\n+\tsetup_dirty_unmergeable &&\n+\ttest_must_fail do_checkout branch2 $HEAD1 &&\n+\ttest_dirty_unmergeable\n+'\n+\n+test_expect_success 'checkout -f -b to a new branch with unmergeable changes discards changes' '\n+\t# still dirty and on branch1\n+\tdo_checkout branch2 $HEAD1 \"-f -b\" &&\n+\ttest_must_fail test_dirty_unmergeable\n+'\n+\n+test_expect_success 'checkout -b to a new branch preserves mergeable changes' '\n+\tgit checkout branch1 &&\n+\n+\t# clean up from previous test\n+\tgit branch -D branch2 &&\n+\n+\tsetup_dirty_mergeable &&\n+\tdo_checkout branch2 $HEAD1 &&\n+\ttest_dirty_mergeable\n+'\n+\n+test_expect_success 'checkout -f -b to a new branch with mergeable changes discards changes' '\n+\t# clean up from previous test\n+\tgit reset --hard &&\n+\n+\tgit checkout branch1 &&\n+\n+\t# clean up from previous test\n+\tgit branch -D branch2 &&\n+\n+\tsetup_dirty_mergeable &&\n+\tdo_checkout branch2 $HEAD1 \"-f -b\" &&\n+\ttest_must_fail test_dirty_mergeable\n+'\n+\n+test_expect_success 'checkout -b to an existing branch fails' '\n+\tgit reset --hard HEAD &&\n+\n+\ttest_must_fail do_checkout branch2 $HEAD2\n+'\n+\n+test_done\n-- \n1.7.1.513.g4f18\n"},{"id":"143981","messageId":"1277140782-4064-3-git-send-email-rctay89@gmail.com","threadId":"24156","inReplyTo":"1277140782-4064-2-git-send-email-rctay89@gmail.com","subject":"[PATCH 2/3] builtin/checkout: change -b from an OPTION_STRING to a OPTION_SET_INT","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-21T17:19:41Z","receivedAt":"2010-06-21T17:19:41Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"This is in preparation for multiple levels of \"-b\".\n\nTo check this change's interaction with --track, the following tests\n(obtained with grep -l \"\\-\\-track\" t/*.sh)were run, and they passed.\n\n  t1507-rev-parse-upstream.sh\n  t3200-branch.sh\n  t5505-remote.sh\n  t5520-pull.sh\n  t6040-tracking-info.sh\n  t7201-co.sh\n\n(t9114-git-svn-dcommit-merge.sh was excluded as I don't have svn.)\n\nSigned-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n builtin/checkout.c |   41 ++++++++++++++++++++++++++---------------\n 1 files changed, 26 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1994be9..e794e1e 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -32,7 +32,8 @@ struct checkout_opts {\n \tint writeout_stage;\n \tint writeout_error;\n \n-\tconst char *new_branch;\n+\tint new_branch;\n+\tconst char *new_branch_name;\n \tconst char *new_orphan_branch;\n \tint new_branch_log;\n \tenum branch_track track;\n@@ -492,7 +493,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n {\n \tstruct strbuf msg = STRBUF_INIT;\n \tconst char *old_desc;\n-\tif (opts->new_branch) {\n+\tif (opts->new_branch_name) {\n \t\tif (opts->new_orphan_branch) {\n \t\t\tif (opts->new_branch_log && !log_all_ref_updates) {\n \t\t\t\tint temp;\n@@ -511,9 +512,9 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t}\n \t\t}\n \t\telse\n-\t\t\tcreate_branch(old->name, opts->new_branch, new->name, 0,\n+\t\t\tcreate_branch(old->name, opts->new_branch_name, new->name, 0,\n \t\t\t\t      opts->new_branch_log, opts->track);\n-\t\tnew->name = opts->new_branch;\n+\t\tnew->name = opts->new_branch_name;\n \t\tsetup_branch_path(new);\n \t}\n \n@@ -531,7 +532,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t\t\tnew->name);\n \t\t\telse\n \t\t\t\tfprintf(stderr, \"Switched to%s branch '%s'\\n\",\n-\t\t\t\t\topts->new_branch ? \" a new\" : \"\",\n+\t\t\t\t\topts->new_branch_name ? \" a new\" : \"\",\n \t\t\t\t\tnew->name);\n \t\t}\n \t\tif (old->path && old->name) {\n@@ -657,7 +658,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tint dwim_new_local_branch = 1;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts.quiet),\n-\t\tOPT_STRING('b', NULL, &opts.new_branch, \"new branch\", \"branch\"),\n+\t\tOPT_SET_INT('b', NULL, &opts.new_branch,\n+\t\t\t    \"create and checkout a new branch\", 1),\n \t\tOPT_BOOLEAN('l', NULL, &opts.new_branch_log, \"log for new branch\"),\n \t\tOPT_SET_INT('t', \"track\",  &opts.track, \"track\",\n \t\t\tBRANCH_TRACK_EXPLICIT),\n@@ -692,8 +694,17 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t   || opts.new_branch_log || opts.merge || opts.force))\n \t\tdie (\"--patch is incompatible with all other options\");\n \n+\tif (opts.new_branch > 0) {\n+\t\tconst char *arg = argv[0];\n+\t\tif (!argc || !strcmp(arg, \"--\"))\n+\t\t\tdie (\"Missing branch name\");\n+\t\topts.new_branch_name = arg;\n+\t\targv++;\n+\t\targc--;\n+\t}\n+\n \t/* --track without -b should DWIM */\n-\tif (0 < opts.track && !opts.new_branch) {\n+\tif (0 < opts.track && !opts.new_branch_name) {\n \t\tconst char *argv0 = argv[0];\n \t\tif (!argc || !strcmp(argv0, \"--\"))\n \t\t\tdie (\"--track needs a branch name\");\n@@ -704,7 +715,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_name = argv0 + 1;\n \t}\n \n \tif (opts.new_orphan_branch) {\n@@ -712,7 +723,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\tdie(\"--orphan and -b are mutually exclusive\");\n \t\tif (opts.track > 0)\n \t\t\tdie(\"--orphan cannot be used with -t\");\n-\t\topts.new_branch = opts.new_orphan_branch;\n+\t\topts.new_branch_name = opts.new_orphan_branch;\n \t}\n \n \tif (conflict_style) {\n@@ -771,13 +782,13 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\tif (!patch_mode &&\n \t\t\t    dwim_new_local_branch &&\n \t\t\t    opts.track == BRANCH_TRACK_UNSPECIFIED &&\n-\t\t\t    !opts.new_branch &&\n+\t\t\t    !opts.new_branch_name &&\n \t\t\t    !check_filename(NULL, arg) &&\n \t\t\t    argc == 1) {\n \t\t\t\tconst char *remote = unique_tracking_name(arg);\n \t\t\t\tif (!remote || get_sha1(remote, rev))\n \t\t\t\t\tgoto no_reference;\n-\t\t\t\topts.new_branch = arg;\n+\t\t\t\topts.new_branch_name = arg;\n \t\t\t\targ = remote;\n \t\t\t\t/* DWIMmed to create local branch */\n \t\t\t}\n@@ -853,13 +864,13 @@ no_reference:\n \tif (patch_mode)\n \t\treturn interactive_checkout(new.name, NULL, &opts);\n \n-\tif (opts.new_branch) {\n+\tif (opts.new_branch_name) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tif (strbuf_check_branch_ref(&buf, opts.new_branch))\n+\t\tif (strbuf_check_branch_ref(&buf, opts.new_branch_name))\n \t\t\tdie(\"git checkout: we do not like '%s' as a branch name.\",\n-\t\t\t    opts.new_branch);\n+\t\t\t    opts.new_branch_name);\n \t\tif (!get_sha1(buf.buf, rev))\n-\t\t\tdie(\"git checkout: branch %s already exists\", opts.new_branch);\n+\t\t\tdie(\"git checkout: branch %s already exists\", opts.new_branch_name);\n \t\tstrbuf_release(&buf);\n \t}\n \n-- \n1.7.1.513.g4f18\n"},{"id":"143982","messageId":"1277140782-4064-4-git-send-email-rctay89@gmail.com","threadId":"24156","inReplyTo":"1277140782-4064-3-git-send-email-rctay89@gmail.com","subject":"[PATCH 3/3] builtin/checkout: learn -B","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-21T17:19:42Z","receivedAt":"2010-06-21T17:19:42Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n Documentation/git-checkout.txt |   15 +++++++++++-\n builtin/checkout.c             |    7 ++++-\n t/t2018-checkout-branch.sh     |   45 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 63 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 261dd90..5849e13 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -9,7 +9,7 @@ SYNOPSIS\n --------\n [verse]\n 'git checkout' [-q] [-f] [-m] [<branch>]\n-'git checkout' [-q] [-f] [-m] [[-b|--orphan] <new_branch>] [<start_point>]\n+'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]\n 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...\n 'git checkout' --patch [<tree-ish>] [--] [<paths>...]\n \n@@ -21,7 +21,7 @@ also update `HEAD` to set the specified branch as the current\n branch.\n \n 'git checkout' [<branch>]::\n-'git checkout' -b <new branch> [<start point>]::\n+'git checkout' -b|-B <branch> [<start point>]::\n \n \tThis form switches branches by updating the index, working\n \ttree, and HEAD to reflect the specified branch.\n@@ -31,6 +31,13 @@ were called and then checked out; in this case you can\n use the `--track` or `--no-track` options, which will be passed to\n 'git branch'.  As a convenience, `--track` without `-b` implies branch\n creation; see the description of `--track` below.\n++\n+If `-B` is given, <branch> is created if it doesn't exist; otherwise, it\n+is reset. This is equivalent to\n++\n+------------\n+$ git branch -f <branch> [<start point>] && git checkout <branch>\n+------------\n \n 'git checkout' [--patch] [<tree-ish>] [--] <pathspec>...::\n \n@@ -75,6 +82,10 @@ entries; instead, unmerged entries are ignored.\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+-B::\n+\tChecks out to the branch named <branch>, creating it if it does\n+\tnot exist; otherwise, the branch is reset.\n+\n -t::\n --track::\n \tWhen creating a new branch, set up \"upstream\" configuration. See\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex e794e1e..f7c2cdd 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -512,7 +512,8 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t}\n \t\t}\n \t\telse\n-\t\t\tcreate_branch(old->name, opts->new_branch_name, new->name, 0,\n+\t\t\tcreate_branch(old->name, opts->new_branch_name, new->name,\n+\t\t\t\t      opts->new_branch > 1,\n \t\t\t\t      opts->new_branch_log, opts->track);\n \t\tnew->name = opts->new_branch_name;\n \t\tsetup_branch_path(new);\n@@ -660,6 +661,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT__QUIET(&opts.quiet),\n \t\tOPT_SET_INT('b', NULL, &opts.new_branch,\n \t\t\t    \"create and checkout a new branch\", 1),\n+\t\tOPT_SET_INT('B', NULL, &opts.new_branch,\n+\t\t\t    \"create and checkout a branch, resetting it if it exists\", 2),\n \t\tOPT_BOOLEAN('l', NULL, &opts.new_branch_log, \"log for new branch\"),\n \t\tOPT_SET_INT('t', \"track\",  &opts.track, \"track\",\n \t\t\tBRANCH_TRACK_EXPLICIT),\n@@ -869,7 +872,7 @@ no_reference:\n \t\tif (strbuf_check_branch_ref(&buf, opts.new_branch_name))\n \t\t\tdie(\"git checkout: we do not like '%s' as a branch name.\",\n \t\t\t    opts.new_branch_name);\n-\t\tif (!get_sha1(buf.buf, rev))\n+\t\tif (!get_sha1(buf.buf, rev) && !(opts.new_branch > 1))\n \t\t\tdie(\"git checkout: branch %s already exists\", opts.new_branch_name);\n \t\tstrbuf_release(&buf);\n \t}\ndiff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh\nindex 3c13065..1caffea 100755\n--- a/t/t2018-checkout-branch.sh\n+++ b/t/t2018-checkout-branch.sh\n@@ -118,4 +118,49 @@ test_expect_success 'checkout -b to an existing branch fails' '\n \ttest_must_fail do_checkout branch2 $HEAD2\n '\n \n+test_expect_success 'checkout -B to an existing branch resets branch to HEAD' '\n+\tgit checkout branch1 &&\n+\n+\tdo_checkout branch2 \"\" -B\n+'\n+\n+test_expect_success 'checkout -B to an existing branch with an explicit ref resets branch to that ref' '\n+\tgit checkout branch1 &&\n+\n+\tdo_checkout branch2 $HEAD1 -B\n+'\n+\n+test_expect_success 'checkout -B to an existing branch with unmergeable changes fails' '\n+\tgit checkout branch1 &&\n+\n+\tsetup_dirty_unmergeable &&\n+\ttest_must_fail do_checkout branch2 $HEAD1 -B &&\n+\ttest_dirty_unmergeable\n+'\n+\n+test_expect_success 'checkout -f -B to an existing branch with unmergeable changes discards changes' '\n+\t# still dirty and on branch1\n+\tdo_checkout branch2 $HEAD1 \"-f -B\" &&\n+\ttest_must_fail test_dirty_unmergeable\n+'\n+\n+test_expect_success 'checkout -B to an existing branch preserves mergeable changes' '\n+\tgit checkout branch1 &&\n+\n+\tsetup_dirty_mergeable &&\n+\tdo_checkout branch2 $HEAD1 -B &&\n+\ttest_dirty_mergeable\n+'\n+\n+test_expect_success 'checkout -f -B to an existing branch with mergeable changes discards changes' '\n+\t# clean up from previous test\n+\tgit reset --hard &&\n+\n+\tgit checkout branch1 &&\n+\n+\tsetup_dirty_mergeable &&\n+\tdo_checkout branch2 $HEAD1 \"-f -B\" &&\n+\ttest_must_fail test_dirty_mergeable\n+'\n+\n test_done\n-- \n1.7.1.513.g4f18\n"},{"id":"143983","messageId":"AANLkTikBFKdeuqQZOTRzxKE_6nR3bNDDsEtGIJVT0nhz@mail.gmail.com","threadId":"24156","inReplyTo":"1277140782-4064-1-git-send-email-rctay89@gmail.com","subject":"Re: [PATCH 0/3] teach git-checkout -B WAS builtin/checkout: DWIM with -f -b","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-21T17:24:06Z","receivedAt":"2010-06-21T17:24:06Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"On Tue, Jun 22, 2010 at 1:19 AM, Tay Ray Chuan <rctay89@gmail.com> wrote:\n> Right now, the -f/--force in git-checkout doesn't carry over to -b\n> operations - ie. you can't do git checkout -b branch on an existing\n> branch.\n>\n> This patch allows you to do this with the -B option, so that one can do\n>\n>  git checkout -B branch ref\n>\n> which is equivalent to\n>\n>  git branch -f branch ref && git checkout branch\n\noops - forgot to add this to the cover letter:\n\nJeff, I've tried my best to check that the --track DWIM-ery still\nworks; could you second this?\n\nErick, does this series change how --orphan should be used?\n\n-- \nCheers,\nRay Chuan\n"},{"id":"144000","messageId":"AANLkTimvSubp07W7ylRZVOrBTYtfsdkD6R5HRIiz080C@mail.gmail.com","threadId":"24156","inReplyTo":"AANLkTikBFKdeuqQZOTRzxKE_6nR3bNDDsEtGIJVT0nhz@mail.gmail.com","subject":"Re: [PATCH 0/3] teach git-checkout -B WAS builtin/checkout: DWIM with -f -b","fromName":"Erick Mattos","fromEmail":"erick.mattos@gmail.com","sentAt":"2010-06-21T21:30:27Z","receivedAt":"2010-06-21T21:30:27Z","isPatch":true,"sender":{"key":"erick.mattos@gmail.com","avatar":"https://avatars.githubusercontent.com/u/134001?v=4"},"body":"Hi,\n\n2010/6/21 Tay Ray Chuan <rctay89@gmail.com>:\n> On Tue, Jun 22, 2010 at 1:19 AM, Tay Ray Chuan <rctay89@gmail.com> wrote:\n>> Right now, the -f/--force in git-checkout doesn't carry over to -b\n>> operations - ie. you can't do git checkout -b branch on an existing\n>> branch.\n>>\n>> This patch allows you to do this with the -B option, so that one can do\n>>\n>>  git checkout -B branch ref\n>>\n>> which is equivalent to\n>>\n>>  git branch -f branch ref && git checkout branch\n\nIf I need to do that, I would do: git checkout branch && git reset --hard ref.\n\nAnother way to do it is: git branch -D branch && git checkout -b branch ref.\n\nSo, we have enough porcelain commands to do it.  I would recommend a\nsecond thought to see if your refinement is really necessary.  I would\nagree so if those procedures are needed very often.\n\n> Erick, does this series change how --orphan should be used?\n\nIf you be careful It won't make any change.  I will comment that on\nthe series if needed.\n\nThanks very much for asking.\n\nKind regards\n"},{"id":"144017","messageId":"AANLkTikyugBxwyCu0GY4dTLW6n_OQhIPNnPfd0_i6k0t@mail.gmail.com","threadId":"24156","inReplyTo":"1277140782-4064-3-git-send-email-rctay89@gmail.com","subject":"Re: [PATCH 2/3] builtin/checkout: change -b from an OPTION_STRING to a OPTION_SET_INT","fromName":"Erick Mattos","fromEmail":"erick.mattos@gmail.com","sentAt":"2010-06-21T23:04:30Z","receivedAt":"2010-06-21T23:04:30Z","isPatch":true,"sender":{"key":"erick.mattos@gmail.com","avatar":"https://avatars.githubusercontent.com/u/134001?v=4"},"body":"Hi,\n\n2010/6/21 Tay Ray Chuan <rctay89@gmail.com>:\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 1994be9..e794e1e 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -32,7 +32,8 @@ struct checkout_opts {\n>        int writeout_stage;\n>        int writeout_error;\n>\n> -       const char *new_branch;\n> +       int new_branch;\n> +       const char *new_branch_name;\n\nThe change of name of the existent variable creates more hassle than\nhelps.\n\nAs you are adding a new option I suggest you to create a new\nvariable named new_branch_forced or whatever.  This way you avoid\nmaking a lot of changes as you did and minimize the possibility of\nadding new bugs by not catching all the problems affected by the\nname change.\n\nI think you have chosen to do that just because of the variable\nnames then I think you should find a variable naming alternative to\nsatisfy you without changing existing ones.\n\nMy suggestion is to do the same I did with --orphan\n(const char *new_orphan_branch):\n  * Create a C string variable that receives its data by a new\n    OPT_STRING.\n  * After making all tests needed, point new_branch to your created\n    variable.\n  * You will always know if your new option was used or not by\n    checking nullity of the just created C string.\n\nThis way any existing implementation remains untouched and thus it\nis much more easy to avoid bugs.\n\n> @@ -692,8 +694,17 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>                           || opts.new_branch_log || opts.merge || opts.force))\n>                die (\"--patch is incompatible with all other options\");\n>\n> +       if (opts.new_branch > 0) {\n> +               const char *arg = argv[0];\n> +               if (!argc || !strcmp(arg, \"--\"))\n> +                       die (\"Missing branch name\");\n> +               opts.new_branch_name = arg;\n> +               argv++;\n> +               argc--;\n> +       }\n\nYou won't use this if you accept my previous suggestions.\n\nTo conclude, IMHO, I don't think this patch is a good thing to do.\n/* I would like to point out that I am criticizing it but also\n   presenting suggestions!  So it is a constructive critic!  ;-) */\n\nBest regards\n"},{"id":"144087","messageId":"7vr5jxegvd.fsf@alter.siamese.dyndns.org","threadId":"24156","inReplyTo":"1277140782-4064-4-git-send-email-rctay89@gmail.com","subject":"Re: [PATCH 3/3] builtin/checkout: learn -B","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-06-23T18:36:06Z","receivedAt":"2010-06-23T18:36:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Tay Ray Chuan <rctay89@gmail.com> writes:\n\n> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\n> index 261dd90..5849e13 100644\n> --- a/Documentation/git-checkout.txt\n> +++ b/Documentation/git-checkout.txt\n> @@ -31,6 +31,13 @@ were called and then checked out; in this case you can\n>  use the `--track` or `--no-track` options, which will be passed to\n>  'git branch'.  As a convenience, `--track` without `-b` implies branch\n>  creation; see the description of `--track` below.\n> ++\n> +If `-B` is given, <branch> is created if it doesn't exist; otherwise, it\n> +is reset. This is equivalent to\n> ++\n> +------------\n> +$ git branch -f <branch> [<start point>] && git checkout <branch>\n> +------------\n\nIt is somewhat sad if these are truly equivalent.  In the above sequence,\n\"git checkout <branch>\" could stop to prevent you from clobbering your\nlocal changes, but at that point the branch has already been updated.\n\nI was hoping that the check (and stop) can be done first and then the\nbranch head is cloberred after you know you will succeed the checkout.\n"},{"id":"144088","messageId":"AANLkTikYgbVlyLlaba2G2CTFc0wFgylPRvhm-BX0igFz@mail.gmail.com","threadId":"24156","inReplyTo":"AANLkTikyugBxwyCu0GY4dTLW6n_OQhIPNnPfd0_i6k0t@mail.gmail.com","subject":"Re: [PATCH 2/3] builtin/checkout: change -b from an OPTION_STRING to a OPTION_SET_INT","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-23T19:04:41Z","receivedAt":"2010-06-23T19:04:41Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Hi,\n\nOn Tue, Jun 22, 2010 at 7:04 AM, Erick Mattos <erick.mattos@gmail.com> wrote:\n> Hi,\n>\n> 2010/6/21 Tay Ray Chuan <rctay89@gmail.com>:\n>> diff --git a/builtin/checkout.c b/builtin/checkout.c\n>> index 1994be9..e794e1e 100644\n>> --- a/builtin/checkout.c\n>> +++ b/builtin/checkout.c\n>> @@ -32,7 +32,8 @@ struct checkout_opts {\n>>        int writeout_stage;\n>>        int writeout_error;\n>>\n>> -       const char *new_branch;\n>> +       int new_branch;\n>> +       const char *new_branch_name;\n>\n> The change of name of the existent variable creates more hassle than\n> helps.\n>\n> As you are adding a new option I suggest you to create a new\n> variable named new_branch_forced or whatever.  This way you avoid\n> making a lot of changes as you did and minimize the possibility of\n> adding new bugs by not catching all the problems affected by the\n> name change.\n\nErick, thanks for the suggestion - I've a new iteration that takes\nsome of your points.\n\n-- \nCheers,\nRay Chuan\n"},{"id":"144089","messageId":"AANLkTinBBuxPKiflz1qMvbuNk8ACSN8IjVcAbefawony@mail.gmail.com","threadId":"24156","inReplyTo":"7vr5jxegvd.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 3/3] builtin/checkout: learn -B","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-23T19:13:25Z","receivedAt":"2010-06-23T19:13:25Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"On Thu, Jun 24, 2010 at 2:36 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Tay Ray Chuan <rctay89@gmail.com> writes:\n>\n>> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\n>> index 261dd90..5849e13 100644\n>> --- a/Documentation/git-checkout.txt\n>> +++ b/Documentation/git-checkout.txt\n>> @@ -31,6 +31,13 @@ were called and then checked out; in this case you can\n>>  use the `--track` or `--no-track` options, which will be passed to\n>>  'git branch'.  As a convenience, `--track` without `-b` implies branch\n>>  creation; see the description of `--track` below.\n>> ++\n>> +If `-B` is given, <branch> is created if it doesn't exist; otherwise, it\n>> +is reset. This is equivalent to\n>> ++\n>> +------------\n>> +$ git branch -f <branch> [<start point>] && git checkout <branch>\n>> +------------\n>\n> It is somewhat sad if these are truly equivalent.  In the above sequence,\n> \"git checkout <branch>\" could stop to prevent you from clobbering your\n> local changes, but at that point the branch has already been updated.\n>\n> I was hoping that the check (and stop) can be done first and then the\n> branch head is cloberred after you know you will succeed the checkout.\n\nhmm - would\n\n  git checkout <branch> && git reset --keep <start point>\n\nmake the cut?\n\n-- \nCheers,\nRay Chuan\n"},{"id":"144092","messageId":"1277321340-4000-1-git-send-email-rctay89@gmail.com","threadId":"24156","inReplyTo":"1277140782-4064-1-git-send-email-rctay89@gmail.com","subject":"[PATCH v2 0/3] teach git-checkout -B WAS builtin/checkout: DWIM with -f -b","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-23T19:28:57Z","receivedAt":"2010-06-23T19:28:57Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Right now, the -f/--force in git-checkout doesn't carry over to -b\noperations - ie. you can't do git checkout -b branch on an existing\nbranch.\n\nThis patch allows you to do this with the -B option, so that one can do\n\n  git checkout -B branch ref\n\nwhich is equivalent to\n\n  git branch -f branch ref && git checkout branch\n\nChanges from v1:\n - reworked option handling\n - don't replace <new_branch> with <branch> in documentation\n\nContents:\n[PATCH v2 1/3] add tests for checkout -b\n[PATCH v2 2/3] builtin/checkout: reword hint for -b\n[PATCH v2 3/3] builtin/checkout: learn -B\n\n Documentation/git-checkout.txt |   21 +++++-\n builtin/checkout.c             |   32 ++++++--\n t/t2018-checkout-branch.sh     |  166 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 211 insertions(+), 8 deletions(-)\n create mode 100755 t/t2018-checkout-branch.sh\n"},{"id":"144093","messageId":"1277321340-4000-2-git-send-email-rctay89@gmail.com","threadId":"24156","inReplyTo":"1277321340-4000-1-git-send-email-rctay89@gmail.com","subject":"[PATCH v2 1/3] add tests for checkout -b","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-23T19:28:58Z","receivedAt":"2010-06-23T19:28:58Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n t/t2018-checkout-branch.sh |  121 ++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 121 insertions(+), 0 deletions(-)\n create mode 100755 t/t2018-checkout-branch.sh\n\ndiff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh\nnew file mode 100755\nindex 0000000..3c13065\n--- /dev/null\n+++ b/t/t2018-checkout-branch.sh\n@@ -0,0 +1,121 @@\n+#!/bin/sh\n+\n+test_description='checkout '\n+\n+. ./test-lib.sh\n+\n+# Arguments: <branch> <sha> [<checkout options>]\n+#\n+# Runs \"git checkout\" to switch to <branch>, testing that\n+#\n+#   1) we are on the specified branch, <branch>;\n+#   2) HEAD is <sha>; if <sha> is not specified, the old HEAD is used.\n+#\n+# If <checkout options> is not specified, \"git checkout\" is run with -b.\n+do_checkout() {\n+\texp_branch=$1 &&\n+\texp_ref=\"refs/heads/$exp_branch\" &&\n+\n+\t# if <sha> is not specified, use HEAD.\n+\texp_sha=${2:-$(git rev-parse --verify HEAD)} &&\n+\n+\t# default options for git checkout: -b\n+\tif [ -z \"$3\" ]; then\n+\t\topts=\"-b\"\n+\telse\n+\t\topts=\"$3\"\n+\tfi\n+\n+\tgit checkout $opts $exp_branch $exp_sha &&\n+\n+\ttest $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&\n+\ttest $exp_sha = $(git rev-parse --verify HEAD)\n+}\n+\n+test_dirty_unmergeable() {\n+\t! git diff --exit-code >/dev/null\n+}\n+\n+setup_dirty_unmergeable() {\n+\techo >>file1 change2\n+}\n+\n+test_dirty_mergeable() {\n+\t! git diff --cached --exit-code >/dev/null\n+}\n+\n+setup_dirty_mergeable() {\n+\techo >file2 file2 &&\n+\tgit add file2\n+}\n+\n+test_expect_success 'setup' '\n+\ttest_commit initial file1 &&\n+\tHEAD1=$(git rev-parse --verify HEAD) &&\n+\n+\ttest_commit change1 file1 &&\n+\tHEAD2=$(git rev-parse --verify HEAD) &&\n+\n+\tgit branch -m branch1\n+'\n+\n+test_expect_success 'checkout -b to a new branch, set to HEAD' '\n+\tdo_checkout branch2\n+'\n+\n+test_expect_success 'checkout -b to a new branch, set to an explicit ref' '\n+\tgit checkout branch1 &&\n+\tgit branch -D branch2 &&\n+\n+\tdo_checkout branch2 $HEAD1\n+'\n+\n+test_expect_success 'checkout -b to a new branch with unmergeable changes fails' '\n+\tgit checkout branch1 &&\n+\n+\t# clean up from previous test\n+\tgit branch -D branch2 &&\n+\n+\tsetup_dirty_unmergeable &&\n+\ttest_must_fail do_checkout branch2 $HEAD1 &&\n+\ttest_dirty_unmergeable\n+'\n+\n+test_expect_success 'checkout -f -b to a new branch with unmergeable changes discards changes' '\n+\t# still dirty and on branch1\n+\tdo_checkout branch2 $HEAD1 \"-f -b\" &&\n+\ttest_must_fail test_dirty_unmergeable\n+'\n+\n+test_expect_success 'checkout -b to a new branch preserves mergeable changes' '\n+\tgit checkout branch1 &&\n+\n+\t# clean up from previous test\n+\tgit branch -D branch2 &&\n+\n+\tsetup_dirty_mergeable &&\n+\tdo_checkout branch2 $HEAD1 &&\n+\ttest_dirty_mergeable\n+'\n+\n+test_expect_success 'checkout -f -b to a new branch with mergeable changes discards changes' '\n+\t# clean up from previous test\n+\tgit reset --hard &&\n+\n+\tgit checkout branch1 &&\n+\n+\t# clean up from previous test\n+\tgit branch -D branch2 &&\n+\n+\tsetup_dirty_mergeable &&\n+\tdo_checkout branch2 $HEAD1 \"-f -b\" &&\n+\ttest_must_fail test_dirty_mergeable\n+'\n+\n+test_expect_success 'checkout -b to an existing branch fails' '\n+\tgit reset --hard HEAD &&\n+\n+\ttest_must_fail do_checkout branch2 $HEAD2\n+'\n+\n+test_done\n--\n1.7.1.513.g4f18\n"},{"id":"144095","messageId":"1277321340-4000-3-git-send-email-rctay89@gmail.com","threadId":"24156","inReplyTo":"1277321340-4000-2-git-send-email-rctay89@gmail.com","subject":"[PATCH v2 2/3] builtin/checkout: reword hint for -b","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-23T19:28:59Z","receivedAt":"2010-06-23T19:28:59Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Shift the 'new' from the param to the hint, and expand the hint.\n\nSigned-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n builtin/checkout.c |    3 ++-\n 1 files changed, 2 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1994be9..3969683 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -657,7 +657,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tint dwim_new_local_branch = 1;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts.quiet),\n-\t\tOPT_STRING('b', NULL, &opts.new_branch, \"new branch\", \"branch\"),\n+\t\tOPT_STRING('b', NULL, &opts.new_branch, \"branch\",\n+\t\t           \"create and checkout a new branch\"),\n \t\tOPT_BOOLEAN('l', NULL, &opts.new_branch_log, \"log for new branch\"),\n \t\tOPT_SET_INT('t', \"track\",  &opts.track, \"track\",\n \t\t\tBRANCH_TRACK_EXPLICIT),\n--\n1.7.1.513.g4f18\n"},{"id":"144094","messageId":"1277321340-4000-4-git-send-email-rctay89@gmail.com","threadId":"24156","inReplyTo":"1277321340-4000-3-git-send-email-rctay89@gmail.com","subject":"[PATCH v2 3/3] builtin/checkout: learn -B","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-06-23T19:29:00Z","receivedAt":"2010-06-23T19:29:00Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Internally, --track and --orphan still use the 'safe' -b, not -B.\n\nSigned-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n\nJunio, I didn't wait for your reply to my suggestion about using\n\"git reset\" instead, as I hope to put this out for review before I creep\nto bed - I'll put it in the next iteration, if need be.\n\n Documentation/git-checkout.txt |   21 ++++++++++++++++-\n builtin/checkout.c             |   29 +++++++++++++++++++++----\n t/t2018-checkout-branch.sh     |   45 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 88 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 261dd90..97c5144 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -9,7 +9,7 @@ SYNOPSIS\n --------\n [verse]\n 'git checkout' [-q] [-f] [-m] [<branch>]\n-'git checkout' [-q] [-f] [-m] [[-b|--orphan] <new_branch>] [<start_point>]\n+'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]\n 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...\n 'git checkout' --patch [<tree-ish>] [--] [<paths>...]\n\n@@ -21,7 +21,7 @@ also update `HEAD` to set the specified branch as the current\n branch.\n\n 'git checkout' [<branch>]::\n-'git checkout' -b <new branch> [<start point>]::\n+'git checkout' -b|-B <new_branch> [<start point>]::\n\n \tThis form switches branches by updating the index, working\n \ttree, and HEAD to reflect the specified branch.\n@@ -31,6 +31,17 @@ were called and then checked out; in this case you can\n use the `--track` or `--no-track` options, which will be passed to\n 'git branch'.  As a convenience, `--track` without `-b` implies branch\n creation; see the description of `--track` below.\n++\n+If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it\n+is reset. This is the transactional equivalent of\n++\n+------------\n+$ git branch -f <branch> [<start point>]\n+$ git checkout <branch>\n+------------\n++\n+that is to say, the branch is not reset/created unless \"git checkout\" is\n+successful.\n\n 'git checkout' [--patch] [<tree-ish>] [--] <pathspec>...::\n\n@@ -75,6 +86,12 @@ entries; instead, unmerged entries are ignored.\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+-B::\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\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 3969683..ec2ecfa 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -32,7 +32,11 @@ struct checkout_opts {\n \tint writeout_stage;\n \tint writeout_error;\n\n+\t/* not set by parse_options */\n+\tint branch_exists;\n+\n \tconst char *new_branch;\n+\tconst char *new_branch_force;\n \tconst char *new_orphan_branch;\n \tint new_branch_log;\n \tenum branch_track track;\n@@ -511,7 +515,8 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t}\n \t\t}\n \t\telse\n-\t\t\tcreate_branch(old->name, opts->new_branch, new->name, 0,\n+\t\t\tcreate_branch(old->name, opts->new_branch, new->name,\n+\t\t\t\t      opts->new_branch_force ? 1 : 0,\n \t\t\t\t      opts->new_branch_log, opts->track);\n \t\tnew->name = opts->new_branch;\n \t\tsetup_branch_path(new);\n@@ -531,7 +536,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t\t\tnew->name);\n \t\t\telse\n \t\t\t\tfprintf(stderr, \"Switched to%s branch '%s'\\n\",\n-\t\t\t\t\topts->new_branch ? \" a new\" : \"\",\n+\t\t\t\t\topts->branch_exists ? \" and reset\" : \" a new\",\n \t\t\t\t\tnew->name);\n \t\t}\n \t\tif (old->path && old->name) {\n@@ -659,6 +664,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT__QUIET(&opts.quiet),\n \t\tOPT_STRING('b', NULL, &opts.new_branch, \"branch\",\n \t\t           \"create and checkout a new branch\"),\n+\t\tOPT_STRING('B', NULL, &opts.new_branch_force, \"branch\",\n+\t\t           \"create/reset and checkout a branch\"),\n \t\tOPT_BOOLEAN('l', NULL, &opts.new_branch_log, \"log for new branch\"),\n \t\tOPT_SET_INT('t', \"track\",  &opts.track, \"track\",\n \t\t\tBRANCH_TRACK_EXPLICIT),\n@@ -689,6 +696,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n\n+\t/* we can assume from now on new_branch = !new_branch_force */\n+\tif (opts.new_branch && opts.new_branch_force)\n+\t\tdie(\"-B cannot be used with -b\");\n+\n+\t/* copy -B over to -b, so that we can just check the latter */\n+\tif (opts.new_branch_force)\n+\t\topts.new_branch = opts.new_branch_force;\n+\n \tif (patch_mode && (opts.track > 0 || opts.new_branch\n \t\t\t   || opts.new_branch_log || opts.merge || opts.force))\n \t\tdie (\"--patch is incompatible with all other options\");\n@@ -710,7 +725,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n\n \tif (opts.new_orphan_branch) {\n \t\tif (opts.new_branch)\n-\t\t\tdie(\"--orphan and -b are mutually exclusive\");\n+\t\t\tdie(\"--orphan and -b|-B are mutually exclusive\");\n \t\tif (opts.track > 0)\n \t\t\tdie(\"--orphan cannot be used with -t\");\n \t\topts.new_branch = opts.new_orphan_branch;\n@@ -859,8 +874,12 @@ no_reference:\n \t\tif (strbuf_check_branch_ref(&buf, opts.new_branch))\n \t\t\tdie(\"git checkout: we do not like '%s' as a branch name.\",\n \t\t\t    opts.new_branch);\n-\t\tif (!get_sha1(buf.buf, rev))\n-\t\t\tdie(\"git checkout: branch %s already exists\", opts.new_branch);\n+\t\tif (!get_sha1(buf.buf, rev)) {\n+\t\t\topts.branch_exists = 1;\n+\t\t\tif (!opts.new_branch_force)\n+\t\t\t\tdie(\"git checkout: branch %s already exists\",\n+\t\t\t\t    opts.new_branch);\n+\t\t}\n \t\tstrbuf_release(&buf);\n \t}\n\ndiff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh\nindex 3c13065..1caffea 100755\n--- a/t/t2018-checkout-branch.sh\n+++ b/t/t2018-checkout-branch.sh\n@@ -118,4 +118,49 @@ test_expect_success 'checkout -b to an existing branch fails' '\n \ttest_must_fail do_checkout branch2 $HEAD2\n '\n\n+test_expect_success 'checkout -B to an existing branch resets branch to HEAD' '\n+\tgit checkout branch1 &&\n+\n+\tdo_checkout branch2 \"\" -B\n+'\n+\n+test_expect_success 'checkout -B to an existing branch with an explicit ref resets branch to that ref' '\n+\tgit checkout branch1 &&\n+\n+\tdo_checkout branch2 $HEAD1 -B\n+'\n+\n+test_expect_success 'checkout -B to an existing branch with unmergeable changes fails' '\n+\tgit checkout branch1 &&\n+\n+\tsetup_dirty_unmergeable &&\n+\ttest_must_fail do_checkout branch2 $HEAD1 -B &&\n+\ttest_dirty_unmergeable\n+'\n+\n+test_expect_success 'checkout -f -B to an existing branch with unmergeable changes discards changes' '\n+\t# still dirty and on branch1\n+\tdo_checkout branch2 $HEAD1 \"-f -B\" &&\n+\ttest_must_fail test_dirty_unmergeable\n+'\n+\n+test_expect_success 'checkout -B to an existing branch preserves mergeable changes' '\n+\tgit checkout branch1 &&\n+\n+\tsetup_dirty_mergeable &&\n+\tdo_checkout branch2 $HEAD1 -B &&\n+\ttest_dirty_mergeable\n+'\n+\n+test_expect_success 'checkout -f -B to an existing branch with mergeable changes discards changes' '\n+\t# clean up from previous test\n+\tgit reset --hard &&\n+\n+\tgit checkout branch1 &&\n+\n+\tsetup_dirty_mergeable &&\n+\tdo_checkout branch2 $HEAD1 \"-f -B\" &&\n+\ttest_must_fail test_dirty_mergeable\n+'\n+\n test_done\n--\n1.7.1.513.g4f18\n"},{"id":"144109","messageId":"AANLkTik0-XHGbmCwO9AvETn8bmMIXdHtXLsH45ppkSCK@mail.gmail.com","threadId":"24156","inReplyTo":"AANLkTikYgbVlyLlaba2G2CTFc0wFgylPRvhm-BX0igFz@mail.gmail.com","subject":"Re: [PATCH 2/3] builtin/checkout: change -b from an OPTION_STRING to a OPTION_SET_INT","fromName":"Erick Mattos","fromEmail":"erick.mattos@gmail.com","sentAt":"2010-06-23T21:37:02Z","receivedAt":"2010-06-23T21:37:02Z","isPatch":true,"sender":{"key":"erick.mattos@gmail.com","avatar":"https://avatars.githubusercontent.com/u/134001?v=4"},"body":"Hi,\n\n2010/6/23 Tay Ray Chuan <rctay89@gmail.com>:\n> Erick, thanks for the suggestion - I've a new iteration that takes\n> some of your points.\n>\n> --\n> Cheers,\n> Ray Chuan\n\nYou are welcome.  Good luck then.  As I heard here once: thank you for\nmaking Git better!.\n\nRegards\n"}]}