Right now, the -f/--force in git-checkout doesn't carry over to -b operations - ie. you can't do git checkout -b branch on an existing branch. This patch series changes this, so that one can do git checkout -f -b branch ref which is equivalent to git branch -f branch ref && git checkout branch Contents: [PATCH 1/3] add tests for checkout -b [PATCH 2/3] t2018-checkout-branch.sh: show that checkout -f -b doesn't DWIM [PATCH 3/3] builtin/checkout: DWIM with -f -b Documentation/git-checkout.txt | 3 + builtin/checkout.c | 4 +- t/t2018-checkout-branch.sh | 86 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100755 t/t2018-checkout-branch.sh --
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com> --- t/t2018-checkout-branch.sh | 20 ++++++++++++++++++++ 1 files changed, 20 insertions(+), 0 deletions(-) diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh index 678a34f..e6c0f8c 100755 --- a/t/t2018-checkout-branch.sh +++ b/t/t2018-checkout-branch.sh @@ -63,4 +63,24 @@ test_expect_success 'checkout -b to an existing branch fails' ' test_must_fail do_checkout branch2 $HEAD2 ' +test_expect_failure 'checkout -f -b to an existing branch resets branch' ' + git checkout branch1 && + + do_checkout branch2 "" -f +' + +test_expect_failure 'checkout -f -b to an existing branch resets branch (explicit ref)' ' + git checkout branch1 && + + do_checkout branch2 $HEAD1 -f +' + +test_expect_failure 'checkout -f -b to an existing branch resets branch (dirty) ' ' + git checkout branch1 && + + setup_dirty && + do_checkout branch2 $HEAD1 -f && + test_must_fail test_dirty +' + test_done -- 1.7.1.513.g4f18 --
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com> --- Documentation/git-checkout.txt | 3 +++ builtin/checkout.c | 4 ++-- t/t2018-checkout-branch.sh | 6 +++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 261dd90..d15d224 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -74,6 +74,9 @@ entries; instead, unmerged entries are ignored. -b:: Create a new branch named <new_branch> and start it at <start_point>; see linkgit:git-branch[1] for details. ++ +When used with `-f`, the branch is reset; changes in the index or the +working tree are thrown away too. -t:: --track:: diff --git a/builtin/checkout.c b/builtin/checkout.c index 1994be9..962d938 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -511,7 +511,7 @@ static void update_refs_for_switch(struct checkout_opts *opts, } } else - create_branch(old->name, opts->new_branch, new->name, 0, + create_branch(old->name, opts->new_branch, new->name, opts->force, opts->new_branch_log, opts->track); new->name = opts->new_branch; setup_branch_path(new); @@ -858,7 +858,7 @@ no_reference: if (strbuf_check_branch_ref(&buf, opts.new_branch)) die("git checkout: we do not like '%s' as a branch name.", opts.new_branch); - if (!get_sha1(buf.buf, rev)) + if (!get_sha1(buf.buf, rev) && !opts.force) die("git checkout: branch %s already exists", opts.new_branch); strbuf_release(&buf); } diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh index e6c0f8c..9895ff5 100755 --- a/t/t2018-checkout-branch.sh +++ b/t/t2018-checkout-branch.sh @@ -63,19 +63,19 @@ test_expect_success 'checkout -b to an existing branch fails' ' test_must_fail do_checkout branch2 $HEAD2 ' -test_expect_failure 'checkout -f -b to an existing branch resets branch' ' +test_expect_success 'checkout -f -b to an existing branch ...
I think this is not "DWIM" but is "-f does not work with -b; fix it". Will queue; thanks. --
I am not sure it is fixing a bug. There are really two possible things to be forced during checkout: 1. throw away local changes 2. overwrite an existing branch Right now, "checkout -f" means just (1). This patch makes it _also_ mean (2). Do we want to conflate those two cases? In some sense, it is reasonable for "-f" to mean "force anything that might be questionable". But I wonder if it pays to be a little more conservative given that this is a safety valve we are talking about. The user can of course still split their compound action into: git branch [-f] new_branch old_branch git checkout [-f] new_branch Less convenient, but there is no ambiguity. I dunno. I don't have a strong feeling on how it should be, but I think it is more than a simple bug fix. -Peff --
I don't have anything to add to that, except that in my opinion checkout does more than enough conflating already, and I am therefore strongly against this change. Clemens --
Ok, fair enough.
FWIW, I often end up doing this when rebuilding 'pu' (and 'jch' that is
used internally that keeps a version of 'next' with cleaner history).
... a script to regenerate jch on top of master (and pu on jch)
$ Meta/Reintegrate master..jch >Meta/redo-jch.sh
$ Meta/Reintegrate jch..pu >Meta/redo-pu.sh
... rewind and rebuild jch on top of updated master
$ git checkout -f -b jch master
... error ... oops
$ git branch -f jch master
$ git checkout jch
$ Meta/redo-jch.sh
$ git diff next
... no output---good.
$ git checkout -f -b pu jch
... error ... oops
$ git branch -f pu jch
$ git checkout pu
$ Meta/redo-pu.sh
--
I'm certainly sympathetic, but as I understand it, with this new patch you are doing the equivalent of: git branch -f jch master git checkout -f jch which is different than what you wrote above. For your workflow, I doubt it matters, but it is potentially destructive. -Peff --
Yes; I thought the implication of "-f" to be destructive would be a justification enough, but I agree with you that conflating the two may be a bad idea. When a user says "git checkout -f -b jch" after seeing the command without "-f" fail due to existing "jch", it is quite clear that the user wants to clobber the history of existing "jch" branch (why else would he giving "-f"), but it is not a justification to clobber local changes he has in the index and the work tree. --
Hi, How about doing git checkout -f -f -b <branch> ? By having the user to specify "-f" twice, we're can be really sure that the user wants to 1) throw away local changes, which is what the current "-f" is supposed to do, and 2) reset an existing branch - new behaviour. -- Cheers, Ray Chuan --
That is not the problem I want you to solve. What your patch does solves
only half of my issue as Peff pointed out (it lets me reuse the name of
the existing branch for an unrelated history, but it loses the local
changes I may have in my working tree). IOW, I want to start <branch>
from scratch, based on an half-done work I started from my current branch
in my work tree.
That is what "git checkout -b <branch>" usually gives us, if the name
<branch> is not in use.
I think you were suggesting not to nuke local change with a single -f
only when -b is also in use, but I think that makes things even more
confusing to the users. Sometimes a single -f discards local changes, but
some other times you would need double -f. That will lead to insanity.
Perhaps we would want another option that is similar to -b but lets us
discard the named branch if it exists. Let's call it tentatively -B.
* git checkout -b <branch> [<start-point>]
- starts a new branch <branch> at <start-point> commit;
- attempts three-way merge to keep the local change forward while doing
so, but aborts if it needs a real file-level merge;
- complains and aborts if <branch> already exists.
When -f is used, instead of attempting three-way merge to keep the local
changes, it discards them.
When -m is used, instead of failing when it needs a real file-level merge,
it will attempt CVS/SVN "update" style merge (and can cause conflicts).
When -B is used instead of -b, it does the three-way merge and the usual
rejection upon a file-level merge unless -m is given. If it can go ahead,
but <branch> already exists, then the existing <branch> is discarded and
replaced with the <start-point> commit.
Something like that would reduce the confusion factor and would help the
"recreate an existing branch from scratch" (or "reuse the name of an
existing branch") workflow a bit.
--
Right now, the -f/--force in git-checkout doesn't carry over to -b operations - ie. you can't do git checkout -b branch on an existing branch. This patch allows you to do this with the -B option, so that one can do git checkout -B branch ref which is equivalent to git branch -f branch ref && git checkout branch Contents: [PATCH 1/3] add tests for checkout -b [PATCH 2/3] builtin/checkout: change -b from an OPTION_STRING to a OPTION_SET_INT [PATCH 3/3] builtin/checkout: learn -B Tay Ray Chuan (3): add tests for checkout -b builtin/checkout: change -b from an OPTION_STRING to a OPTION_SET_INT builtin/checkout: learn -B Documentation/git-checkout.txt | 15 +++- builtin/checkout.c | 46 +++++++---- t/t2018-checkout-branch.sh | 166 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+), 18 deletions(-) create mode 100755 t/t2018-checkout-branch.sh --
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
---
t/t2018-checkout-branch.sh | 121 ++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 121 insertions(+), 0 deletions(-)
create mode 100755 t/t2018-checkout-branch.sh
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
new file mode 100755
index 0000000..3c13065
--- /dev/null
+++ b/t/t2018-checkout-branch.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+
+test_description='checkout '
+
+. ./test-lib.sh
+
+# Arguments: <branch> <sha> [<checkout options>]
+#
+# Runs "git checkout" to switch to <branch>, testing that
+#
+# 1) we are on the specified branch, <branch>;
+# 2) HEAD is <sha>; if <sha> is not specified, the old HEAD is used.
+#
+# If <checkout options> is not specified, "git checkout" is run with -b.
+do_checkout() {
+ exp_branch=$1 &&
+ exp_ref="refs/heads/$exp_branch" &&
+
+ # if <sha> is not specified, use HEAD.
+ exp_sha=${2:-$(git rev-parse --verify HEAD)} &&
+
+ # default options for git checkout: -b
+ if [ -z "$3" ]; then
+ opts="-b"
+ else
+ opts="$3"
+ fi
+
+ git checkout $opts $exp_branch $exp_sha &&
+
+ test $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&
+ test $exp_sha = $(git rev-parse --verify HEAD)
+}
+
+test_dirty_unmergeable() {
+ ! git diff --exit-code >/dev/null
+}
+
+setup_dirty_unmergeable() {
+ echo >>file1 change2
+}
+
+test_dirty_mergeable() {
+ ! git diff --cached --exit-code >/dev/null
+}
+
+setup_dirty_mergeable() {
+ echo >file2 file2 &&
+ git add file2
+}
+
+test_expect_success 'setup' '
+ test_commit initial file1 &&
+ HEAD1=$(git rev-parse --verify HEAD) &&
+
+ test_commit change1 file1 &&
+ HEAD2=$(git rev-parse --verify HEAD) &&
+
+ git branch -m branch1
+'
+
+test_expect_success 'checkout -b to a new branch, set to HEAD' '
+ do_checkout branch2
+'
+
+test_expect_success 'checkout -b to a new branch, set to an explicit ref' '
+ git checkout branch1 &&
+ git branch -D branch2 &&
+
+ do_checkout branch2 ...This is in preparation for multiple levels of "-b".
To check this change's interaction with --track, the following tests
(obtained with grep -l "\-\-track" t/*.sh)were run, and they passed.
t1507-rev-parse-upstream.sh
t3200-branch.sh
t5505-remote.sh
t5520-pull.sh
t6040-tracking-info.sh
t7201-co.sh
(t9114-git-svn-dcommit-merge.sh was excluded as I don't have svn.)
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
---
builtin/checkout.c | 41 ++++++++++++++++++++++++++---------------
1 files changed, 26 insertions(+), 15 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1994be9..e794e1e 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -32,7 +32,8 @@ struct checkout_opts {
int writeout_stage;
int writeout_error;
- const char *new_branch;
+ int new_branch;
+ const char *new_branch_name;
const char *new_orphan_branch;
int new_branch_log;
enum branch_track track;
@@ -492,7 +493,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
{
struct strbuf msg = STRBUF_INIT;
const char *old_desc;
- if (opts->new_branch) {
+ if (opts->new_branch_name) {
if (opts->new_orphan_branch) {
if (opts->new_branch_log && !log_all_ref_updates) {
int temp;
@@ -511,9 +512,9 @@ static void update_refs_for_switch(struct checkout_opts *opts,
}
}
else
- create_branch(old->name, opts->new_branch, new->name, 0,
+ create_branch(old->name, opts->new_branch_name, new->name, 0,
opts->new_branch_log, opts->track);
- new->name = opts->new_branch;
+ new->name = opts->new_branch_name;
setup_branch_path(new);
}
@@ -531,7 +532,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
new->name);
else
fprintf(stderr, "Switched to%s branch '%s'\n",
- opts->new_branch ? " a new" : "",
+ opts->new_branch_name ? " a new" : "",
new->name);
}
if (old->path && old->name) {
@@ -657,7 +658,8 @@ int cmd_checkout(int argc, const char ...Signed-off-by: Tay Ray Chuan <rctay89@gmail.com> --- Documentation/git-checkout.txt | 15 +++++++++++- builtin/checkout.c | 7 ++++- t/t2018-checkout-branch.sh | 45 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 261dd90..5849e13 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git checkout' [-q] [-f] [-m] [<branch>] -'git checkout' [-q] [-f] [-m] [[-b|--orphan] <new_branch>] [<start_point>] +'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>] 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>... 'git checkout' --patch [<tree-ish>] [--] [<paths>...] @@ -21,7 +21,7 @@ also update `HEAD` to set the specified branch as the current branch. 'git checkout' [<branch>]:: -'git checkout' -b <new branch> [<start point>]:: +'git checkout' -b|-B <branch> [<start point>]:: This form switches branches by updating the index, working tree, and HEAD to reflect the specified branch. @@ -31,6 +31,13 @@ were called and then checked out; in this case you can use the `--track` or `--no-track` options, which will be passed to 'git branch'. As a convenience, `--track` without `-b` implies branch creation; see the description of `--track` below. ++ +If `-B` is given, <branch> is created if it doesn't exist; otherwise, it +is reset. This is equivalent to ++ +------------ +$ git branch -f <branch> [<start point>] && git checkout <branch> +------------ 'git checkout' [--patch] [<tree-ish>] [--] <pathspec>...:: @@ -75,6 +82,10 @@ entries; instead, unmerged entries are ignored. Create a new branch named <new_branch> and start it at <start_point>; see linkgit:git-branch[1] for details. +-B:: + Checks out to the branch named <branch>, creating it if it does + not exist; otherwise, ...
It is somewhat sad if these are truly equivalent. In the above sequence, "git checkout <branch>" could stop to prevent you from clobbering your local changes, but at that point the branch has already been updated. I was hoping that the check (and stop) can be done first and then the branch head is cloberred after you know you will succeed the checkout. --
hmm - would git checkout <branch> && git reset --keep <start point> make the cut? -- Cheers, Ray Chuan --
Hi,
The change of name of the existent variable creates more hassle than
helps.
As you are adding a new option I suggest you to create a new
variable named new_branch_forced or whatever. This way you avoid
making a lot of changes as you did and minimize the possibility of
adding new bugs by not catching all the problems affected by the
name change.
I think you have chosen to do that just because of the variable
names then I think you should find a variable naming alternative to
satisfy you without changing existing ones.
My suggestion is to do the same I did with --orphan
(const char *new_orphan_branch):
* Create a C string variable that receives its data by a new
OPT_STRING.
* After making all tests needed, point new_branch to your created
variable.
* You will always know if your new option was used or not by
checking nullity of the just created C string.
This way any existing implementation remains untouched and thus it
You won't use this if you accept my previous suggestions.
To conclude, IMHO, I don't think this patch is a good thing to do.
/* I would like to point out that I am criticizing it but also
presenting suggestions! So it is a constructive critic! ;-) */
Best regards
--
Hi, Erick, thanks for the suggestion - I've a new iteration that takes some of your points. -- Cheers, Ray Chuan --
Hi, You are welcome. Good luck then. As I heard here once: thank you for making Git better!. Regards --
oops - forgot to add this to the cover letter: Jeff, I've tried my best to check that the --track DWIM-ery still works; could you second this? Erick, does this series change how --orphan should be used? -- Cheers, Ray Chuan --
Hi, If I need to do that, I would do: git checkout branch && git reset --hard ref. Another way to do it is: git branch -D branch && git checkout -b branch ref. So, we have enough porcelain commands to do it. I would recommend a second thought to see if your refinement is really necessary. I would If you be careful It won't make any change. I will comment that on the series if needed. Thanks very much for asking. Kind regards --
Right now, the -f/--force in git-checkout doesn't carry over to -b operations - ie. you can't do git checkout -b branch on an existing branch. This patch allows you to do this with the -B option, so that one can do git checkout -B branch ref which is equivalent to git branch -f branch ref && git checkout branch Changes from v1: - reworked option handling - don't replace <new_branch> with <branch> in documentation Contents: [PATCH v2 1/3] add tests for checkout -b [PATCH v2 2/3] builtin/checkout: reword hint for -b [PATCH v2 3/3] builtin/checkout: learn -B Documentation/git-checkout.txt | 21 +++++- builtin/checkout.c | 32 ++++++-- t/t2018-checkout-branch.sh | 166 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 8 deletions(-) create mode 100755 t/t2018-checkout-branch.sh --
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
---
t/t2018-checkout-branch.sh | 121 ++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 121 insertions(+), 0 deletions(-)
create mode 100755 t/t2018-checkout-branch.sh
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
new file mode 100755
index 0000000..3c13065
--- /dev/null
+++ b/t/t2018-checkout-branch.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+
+test_description='checkout '
+
+. ./test-lib.sh
+
+# Arguments: <branch> <sha> [<checkout options>]
+#
+# Runs "git checkout" to switch to <branch>, testing that
+#
+# 1) we are on the specified branch, <branch>;
+# 2) HEAD is <sha>; if <sha> is not specified, the old HEAD is used.
+#
+# If <checkout options> is not specified, "git checkout" is run with -b.
+do_checkout() {
+ exp_branch=$1 &&
+ exp_ref="refs/heads/$exp_branch" &&
+
+ # if <sha> is not specified, use HEAD.
+ exp_sha=${2:-$(git rev-parse --verify HEAD)} &&
+
+ # default options for git checkout: -b
+ if [ -z "$3" ]; then
+ opts="-b"
+ else
+ opts="$3"
+ fi
+
+ git checkout $opts $exp_branch $exp_sha &&
+
+ test $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&
+ test $exp_sha = $(git rev-parse --verify HEAD)
+}
+
+test_dirty_unmergeable() {
+ ! git diff --exit-code >/dev/null
+}
+
+setup_dirty_unmergeable() {
+ echo >>file1 change2
+}
+
+test_dirty_mergeable() {
+ ! git diff --cached --exit-code >/dev/null
+}
+
+setup_dirty_mergeable() {
+ echo >file2 file2 &&
+ git add file2
+}
+
+test_expect_success 'setup' '
+ test_commit initial file1 &&
+ HEAD1=$(git rev-parse --verify HEAD) &&
+
+ test_commit change1 file1 &&
+ HEAD2=$(git rev-parse --verify HEAD) &&
+
+ git branch -m branch1
+'
+
+test_expect_success 'checkout -b to a new branch, set to HEAD' '
+ do_checkout branch2
+'
+
+test_expect_success 'checkout -b to a new branch, set to an explicit ref' '
+ git checkout branch1 &&
+ git branch -D branch2 &&
+
+ do_checkout branch2 ...Shift the 'new' from the param to the hint, and expand the hint.
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
---
builtin/checkout.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1994be9..3969683 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -657,7 +657,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
int dwim_new_local_branch = 1;
struct option options[] = {
OPT__QUIET(&opts.quiet),
- OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
+ OPT_STRING('b', NULL, &opts.new_branch, "branch",
+ "create and checkout a new branch"),
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
OPT_SET_INT('t', "track", &opts.track, "track",
BRANCH_TRACK_EXPLICIT),
--
1.7.1.513.g4f18
--
Internally, --track and --orphan still use the 'safe' -b, not -B. Signed-off-by: Tay Ray Chuan <rctay89@gmail.com> --- Junio, I didn't wait for your reply to my suggestion about using "git reset" instead, as I hope to put this out for review before I creep to bed - I'll put it in the next iteration, if need be. Documentation/git-checkout.txt | 21 ++++++++++++++++- builtin/checkout.c | 29 +++++++++++++++++++++---- t/t2018-checkout-branch.sh | 45 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 7 deletions(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 261dd90..97c5144 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git checkout' [-q] [-f] [-m] [<branch>] -'git checkout' [-q] [-f] [-m] [[-b|--orphan] <new_branch>] [<start_point>] +'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>] 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>... 'git checkout' --patch [<tree-ish>] [--] [<paths>...] @@ -21,7 +21,7 @@ also update `HEAD` to set the specified branch as the current branch. 'git checkout' [<branch>]:: -'git checkout' -b <new branch> [<start point>]:: +'git checkout' -b|-B <new_branch> [<start point>]:: This form switches branches by updating the index, working tree, and HEAD to reflect the specified branch. @@ -31,6 +31,17 @@ were called and then checked out; in this case you can use the `--track` or `--no-track` options, which will be passed to 'git branch'. As a convenience, `--track` without `-b` implies branch creation; see the description of `--track` below. ++ +If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it +is reset. This is the transactional equivalent of ++ +------------ +$ git branch -f <branch> [<start point>] +$ git checkout <branch> +------------ ++ +that is to say, the branch ...
I am frequently trying to do this as well, so I would love the checkout -f -b option. However, I think that the desired behavior for checkout -f -b is that it should be equivalent to: git branch -f jch master git checkout jch (without the -f here) rationale: git checkout -b normally preserves the index & the working tree; adding the -f flag to a command that would otherwise succeed should not change its behavior. -- Michel "Walken" Lespinasse A program is never fully debugged until the last user dies. --
Rhaaa, that actually wouldn't work as the git checkout command would fail if there are changes in the index & working tree. What I want here is actually for git checkout -f -b jch to be equivalent to: git branch -D jch (ignore error if jch branch does not exist) At least the rationale still holds :) -- Michel "Walken" Lespinasse A program is never fully debugged until the last user dies. --
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
---
t/t2018-checkout-branch.sh | 66 ++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 66 insertions(+), 0 deletions(-)
create mode 100755 t/t2018-checkout-branch.sh
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
new file mode 100755
index 0000000..678a34f
--- /dev/null
+++ b/t/t2018-checkout-branch.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+test_description='checkout '
+
+. ./test-lib.sh
+
+# Arguments: <branch> <sha> [<checkout options>]
+do_checkout() {
+ exp_branch=$1 &&
+ exp_ref="refs/heads/$exp_branch" &&
+
+ # if <sha> is not specified, use HEAD.
+ exp_sha=${2:-$(git rev-parse --verify HEAD)} &&
+
+ git checkout ${3+"$3"} -b $exp_branch $exp_sha &&
+
+ test $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&
+ test $exp_sha = $(git rev-parse --verify HEAD)
+}
+
+test_dirty() {
+ ! git diff --exit-code >/dev/null
+}
+
+setup_dirty() {
+ echo >>file1 change2
+}
+
+test_expect_success 'setup' '
+ test_commit initial file1 &&
+ HEAD1=$(git rev-parse --verify HEAD) &&
+
+ test_commit change1 file1 &&
+ HEAD2=$(git rev-parse --verify HEAD) &&
+
+ git branch -m branch1
+'
+
+test_expect_success 'checkout -b to a new branch' '
+ do_checkout branch2
+'
+
+test_expect_success 'checkout -b to a new branch (explicit ref)' '
+ git checkout branch1 &&
+ git branch -D branch2 &&
+
+ do_checkout branch2 $HEAD1
+'
+
+test_expect_success 'checkout -b to a new branch (dirty)' '
+ git checkout branch1 &&
+ git branch -D branch2 &&
+
+ setup_dirty &&
+ test_must_fail do_checkout branch2 $HEAD1 &&
+ test_dirty
+'
+
+test_expect_success 'checkout -b to an existing branch fails' '
+ git reset --hard HEAD &&
+ git branch branch2 &&
+
+ test_must_fail do_checkout branch2 $HEAD2
+'
+
+test_done
--
1.7.1.513.g4f18
--
