* "git branch HEAD" dumps core when on detached head (NULL pointer dereference) @ 2013-02-21 12:27 Per Cederqvist 2013-02-21 12:50 ` Duy Nguyen 2013-02-21 14:18 ` [PATCH] branch: segfault fixes and validation Nguyễn Thái Ngọc Duy 0 siblings, 2 replies; 10+ messages in thread From: Per Cederqvist @ 2013-02-21 12:27 UTC (permalink / raw) To: git Running "git branch HEAD" may be a stupid thing to do. It actually was a mistake on my part. Still, I don't think git should dereference a NULL pointer. Tested using git 1.8.1.4 adn 1.8.1.1. Repeat by: mkdir branchcrash || exit 1 cd branchcrash git init touch a; git add a; git commit -m"Added a". touch b; git add b; git commit -m"Added b". git checkout HEAD^ git branch HEAD The last command dumps core. gdb session: gdb /usr/local/bin/git core GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04 Copyright (C) 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". For bug reporting instructions, please see: <http://bugs.launchpad.net/gdb-linaro/>... Reading symbols from /usr/local/bin/git...done. [New LWP 7174] warning: Can't read pathname for load map: Input/output error. [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Core was generated by `git branch HEAD'. Program terminated with signal 11, Segmentation fault. #0 cmd_branch (argc=1, argv=0x7fffe6e2a0f0, prefix=<optimized out>) at builtin/branch.c:919 919 strbuf_addf(&buf, "refs/remotes/%s", branch->name); (gdb) bt #0 cmd_branch (argc=1, argv=0x7fffe6e2a0f0, prefix=<optimized out>) at builtin/branch.c:919 #1 0x00000000004046ac in run_builtin (argv=0x7fffe6e2a0f0, argc=2, p=<optimized out>) at git.c:273 #2 handle_internal_command (argc=2, argv=0x7fffe6e2a0f0) at git.c:434 #3 0x0000000000404df3 in run_argv (argv=0x7fffe6e29f90, argcp=0x7fffe6e29f9c) at git.c:480 #4 main (argc=2, argv=0x7fffe6e2a0f0) at git.c:555 (gdb) p branch $1 = (struct branch *) 0x0 (gdb) quit /ceder ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: "git branch HEAD" dumps core when on detached head (NULL pointer dereference) 2013-02-21 12:27 "git branch HEAD" dumps core when on detached head (NULL pointer dereference) Per Cederqvist @ 2013-02-21 12:50 ` Duy Nguyen 2013-02-21 13:24 ` Per Cederqvist 2013-02-21 14:18 ` [PATCH] branch: segfault fixes and validation Nguyễn Thái Ngọc Duy 1 sibling, 1 reply; 10+ messages in thread From: Duy Nguyen @ 2013-02-21 12:50 UTC (permalink / raw) To: Per Cederqvist; +Cc: git On Thu, Feb 21, 2013 at 7:27 PM, Per Cederqvist <cederp@opera.com> wrote: > Running "git branch HEAD" may be a stupid thing to do. It actually > was a mistake on my part. Still, I don't think git should dereference > a NULL pointer. We should not. Can you make a patch to fix it (with test cases)? You may want to fix the two preceding blocks, "if (new_upstream)" and "if (unset_upstream)", as well. They don't check for NULL branch either. I think we can say something like "detached HEAD is not valid for this operation" before exit. > > Tested using git 1.8.1.4 adn 1.8.1.1. > > Repeat by: > > mkdir branchcrash || exit 1 > cd branchcrash > git init > touch a; git add a; git commit -m"Added a". > touch b; git add b; git commit -m"Added b". > git checkout HEAD^ > git branch HEAD > > The last command dumps core. gdb session: > > gdb /usr/local/bin/git core > GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04 > Copyright (C) 2012 Free Software Foundation, Inc. > License GPLv3+: GNU GPL version 3 or later > <http://gnu.org/licenses/gpl.html> > This is free software: you are free to change and redistribute it. > There is NO WARRANTY, to the extent permitted by law. Type "show copying" > and "show warranty" for details. > This GDB was configured as "x86_64-linux-gnu". > For bug reporting instructions, please see: > <http://bugs.launchpad.net/gdb-linaro/>... > Reading symbols from /usr/local/bin/git...done. > [New LWP 7174] > > warning: Can't read pathname for load map: Input/output error. > [Thread debugging using libthread_db enabled] > Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". > Core was generated by `git branch HEAD'. > Program terminated with signal 11, Segmentation fault. > #0 cmd_branch (argc=1, argv=0x7fffe6e2a0f0, prefix=<optimized out>) > at builtin/branch.c:919 > 919 strbuf_addf(&buf, "refs/remotes/%s", branch->name); > (gdb) bt > #0 cmd_branch (argc=1, argv=0x7fffe6e2a0f0, prefix=<optimized out>) > at builtin/branch.c:919 > #1 0x00000000004046ac in run_builtin (argv=0x7fffe6e2a0f0, argc=2, > p=<optimized out>) at git.c:273 > #2 handle_internal_command (argc=2, argv=0x7fffe6e2a0f0) at git.c:434 > #3 0x0000000000404df3 in run_argv (argv=0x7fffe6e29f90, > argcp=0x7fffe6e29f9c) > at git.c:480 > #4 main (argc=2, argv=0x7fffe6e2a0f0) at git.c:555 > (gdb) p branch > $1 = (struct branch *) 0x0 > (gdb) quit > > /ceder > -- > To unsubscribe from this list: send the line "unsubscribe git" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Duy ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: "git branch HEAD" dumps core when on detached head (NULL pointer dereference) 2013-02-21 12:50 ` Duy Nguyen @ 2013-02-21 13:24 ` Per Cederqvist 2013-02-21 13:32 ` Duy Nguyen 0 siblings, 1 reply; 10+ messages in thread From: Per Cederqvist @ 2013-02-21 13:24 UTC (permalink / raw) To: Duy Nguyen; +Cc: git On 02/21/13 13:50, Duy Nguyen wrote: > On Thu, Feb 21, 2013 at 7:27 PM, Per Cederqvist <cederp@opera.com> wrote: >> Running "git branch HEAD" may be a stupid thing to do. It actually >> was a mistake on my part. Still, I don't think git should dereference >> a NULL pointer. > > We should not. Can you make a patch to fix it (with test cases)? You > may want to fix the two preceding blocks, "if (new_upstream)" and "if > (unset_upstream)", as well. They don't check for NULL branch either. I > think we can say something like "detached HEAD is not valid for this > operation" before exit. Sorry, but isolating the issue reporting it here is about as much time as I can spend on this issue. Learning the coding standard of Git and how to write test cases is not something I'm prepared to do, at least not at the moment. I hope there is a place for users and reporters of bugs in the Git community. /ceder >> >> Tested using git 1.8.1.4 adn 1.8.1.1. >> >> Repeat by: >> >> mkdir branchcrash || exit 1 >> cd branchcrash >> git init >> touch a; git add a; git commit -m"Added a". >> touch b; git add b; git commit -m"Added b". >> git checkout HEAD^ >> git branch HEAD >> >> The last command dumps core. gdb session: >> >> gdb /usr/local/bin/git core >> GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04 >> Copyright (C) 2012 Free Software Foundation, Inc. >> License GPLv3+: GNU GPL version 3 or later >> <http://gnu.org/licenses/gpl.html> >> This is free software: you are free to change and redistribute it. >> There is NO WARRANTY, to the extent permitted by law. Type "show copying" >> and "show warranty" for details. >> This GDB was configured as "x86_64-linux-gnu". >> For bug reporting instructions, please see: >> <http://bugs.launchpad.net/gdb-linaro/>... >> Reading symbols from /usr/local/bin/git...done. >> [New LWP 7174] >> >> warning: Can't read pathname for load map: Input/output error. >> [Thread debugging using libthread_db enabled] >> Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". >> Core was generated by `git branch HEAD'. >> Program terminated with signal 11, Segmentation fault. >> #0 cmd_branch (argc=1, argv=0x7fffe6e2a0f0, prefix=<optimized out>) >> at builtin/branch.c:919 >> 919 strbuf_addf(&buf, "refs/remotes/%s", branch->name); >> (gdb) bt >> #0 cmd_branch (argc=1, argv=0x7fffe6e2a0f0, prefix=<optimized out>) >> at builtin/branch.c:919 >> #1 0x00000000004046ac in run_builtin (argv=0x7fffe6e2a0f0, argc=2, >> p=<optimized out>) at git.c:273 >> #2 handle_internal_command (argc=2, argv=0x7fffe6e2a0f0) at git.c:434 >> #3 0x0000000000404df3 in run_argv (argv=0x7fffe6e29f90, >> argcp=0x7fffe6e29f9c) >> at git.c:480 >> #4 main (argc=2, argv=0x7fffe6e2a0f0) at git.c:555 >> (gdb) p branch >> $1 = (struct branch *) 0x0 >> (gdb) quit >> >> /ceder >> -- >> To unsubscribe from this list: send the line "unsubscribe git" in >> the body of a message to majordomo@vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html > > > ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: "git branch HEAD" dumps core when on detached head (NULL pointer dereference) 2013-02-21 13:24 ` Per Cederqvist @ 2013-02-21 13:32 ` Duy Nguyen 0 siblings, 0 replies; 10+ messages in thread From: Duy Nguyen @ 2013-02-21 13:32 UTC (permalink / raw) To: Per Cederqvist; +Cc: git On Thu, Feb 21, 2013 at 8:24 PM, Per Cederqvist <cederp@opera.com> wrote: > Sorry, but isolating the issue reporting it here is about as much time > as I can spend on this issue. Learning the coding standard of Git and > how to write test cases is not something I'm prepared to do, at least > not at the moment. I hope there is a place for users and reporters of > bugs in the Git community. Sure. No problem. I just thought you might want to finish it off. I'll look into it. -- Duy ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] branch: segfault fixes and validation 2013-02-21 12:27 "git branch HEAD" dumps core when on detached head (NULL pointer dereference) Per Cederqvist 2013-02-21 12:50 ` Duy Nguyen @ 2013-02-21 14:18 ` Nguyễn Thái Ngọc Duy 2013-02-21 17:47 ` Junio C Hamano 1 sibling, 1 reply; 10+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2013-02-21 14:18 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Per Cederqvist, Nguyễn Thái Ngọc Duy branch_get() can return NULL (so far on detached HEAD only) but some code paths in builtin/branch.c cannot deal with that and cause segfaults. Fix it. While at there, make sure to bail out when the user gives 2 or more arguments, but only the first one is processed. Reported-by: Per Cederqvist <cederp@opera.com> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- builtin/branch.c | 20 ++++++++++++++++++++ t/t3200-branch.sh | 21 +++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/builtin/branch.c b/builtin/branch.c index 6371bf9..c1d688e 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -889,6 +889,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix) } else if (new_upstream) { struct branch *branch = branch_get(argv[0]); + if (argc > 1) + die(_("too many branches to set new upstream")); + + if (!branch) + die(_("could not figure out the branch name from '%s'"), + argc == 1 ? argv[0] : "HEAD"); + if (!ref_exists(branch->refname)) die(_("branch '%s' does not exist"), branch->name); @@ -901,6 +908,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix) struct branch *branch = branch_get(argv[0]); struct strbuf buf = STRBUF_INIT; + if (argc > 1) + die(_("too many branches to unset upstream")); + + if (!branch) + die(_("could not figure out the branch name from '%s'"), + argc == 1 ? argv[0] : "HEAD"); + if (!branch_has_merge_config(branch)) { die(_("Branch '%s' has no upstream information"), branch->name); } @@ -916,6 +930,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix) int branch_existed = 0, remote_tracking = 0; struct strbuf buf = STRBUF_INIT; + if (!strcmp(argv[0], "HEAD")) + die(_("it does not make sense to create 'HEAD' manually")); + + if (!branch) + die(_("could not figure out the branch name from '%s'"), argv[0]); + if (kinds != REF_LOCAL_BRANCH) die(_("-a and -r options to 'git branch' do not make sense with a branch name")); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index f3e0e4a..12f1e4a 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -42,6 +42,10 @@ test_expect_success \ 'git branch a/b/c should create a branch' \ 'git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c' +test_expect_success \ + 'git branch HEAD should fail' \ + 'test_must_fail git branch HEAD' + cat >expect <<EOF $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master EOF @@ -388,6 +392,14 @@ test_expect_success \ 'git tag foobar && test_must_fail git branch --track my11 foobar' +test_expect_success '--set-upstream-to fails on multiple branches' \ + 'test_must_fail git branch --set-upstream-to master a b c' + +test_expect_success '--set-upstream-to fails on detached HEAD' \ + 'git checkout HEAD^{} && + test_must_fail git branch --set-upstream-to master && + git checkout -' + test_expect_success 'use --set-upstream-to modify HEAD' \ 'test_config branch.master.remote foo && test_config branch.master.merge foo && @@ -417,6 +429,15 @@ test_expect_success 'test --unset-upstream on HEAD' \ test_must_fail git branch --unset-upstream ' +test_expect_success '--unset-upstream should fail on multiple branches' \ + 'test_must_fail git branch --unset-upstream a b c' + +test_expect_success '--unset-upstream should fail on detached HEAD' \ + 'git checkout HEAD^{} && + test_must_fail git branch --unset-upstream && + git checkout - +' + test_expect_success 'test --unset-upstream on a particular branch' \ 'git branch my15 git branch --set-upstream-to master my14 && -- 1.8.1.2.536.gf441e6d ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] branch: segfault fixes and validation 2013-02-21 14:18 ` [PATCH] branch: segfault fixes and validation Nguyễn Thái Ngọc Duy @ 2013-02-21 17:47 ` Junio C Hamano 2013-02-22 11:47 ` [PATCH v2] " Nguyễn Thái Ngọc Duy 0 siblings, 1 reply; 10+ messages in thread From: Junio C Hamano @ 2013-02-21 17:47 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git, Per Cederqvist Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > branch_get() can return NULL (so far on detached HEAD only)... Do you anticipate any other cases where the API call should validly return NULL? I offhand do not, ... > but some > code paths in builtin/branch.c cannot deal with that and cause > segfaults. Fix it. > > While at there, make sure to bail out when the user gives 2 or more > arguments, but only the first one is processed. > > Reported-by: Per Cederqvist <cederp@opera.com> > Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> > --- > builtin/branch.c | 20 ++++++++++++++++++++ > t/t3200-branch.sh | 21 +++++++++++++++++++++ > 2 files changed, 41 insertions(+) > > diff --git a/builtin/branch.c b/builtin/branch.c > index 6371bf9..c1d688e 100644 > --- a/builtin/branch.c > +++ b/builtin/branch.c > @@ -889,6 +889,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix) > } else if (new_upstream) { > struct branch *branch = branch_get(argv[0]); > > + if (argc > 1) > + die(_("too many branches to set new upstream")); > + > + if (!branch) > + die(_("could not figure out the branch name from '%s'"), > + argc == 1 ? argv[0] : "HEAD"); ... and find this "could not figure out" very unfriendly to the user. Is it a bug in the implementation, silly Git failing to find what branch the user meant? What recourse does the user have at this point? Or is it a user error to ask Git to operate on the branch pointed at by HEAD, when HEAD does not refer to any branch? If that is the case, then the message should say that there is no current branch to operate on because the user is on a detached HEAD. That would point the user in the right direction to correct the user error, no? Of course, argv[0] may not be HEAD and in that case you may have to say "no such branch %s" % argv[0] or something. The point is that "could not figure out" feels a bit too lazy. > @@ -901,6 +908,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix) > struct branch *branch = branch_get(argv[0]); > struct strbuf buf = STRBUF_INIT; > > + if (argc > 1) > + die(_("too many branches to unset upstream")); > + > + if (!branch) > + die(_("could not figure out the branch name from '%s'"), > + argc == 1 ? argv[0] : "HEAD"); Likewise. > @@ -916,6 +930,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix) > int branch_existed = 0, remote_tracking = 0; > struct strbuf buf = STRBUF_INIT; > > + if (!strcmp(argv[0], "HEAD")) > + die(_("it does not make sense to create 'HEAD' manually")); > + > + if (!branch) > + die(_("could not figure out the branch name from '%s'"), argv[0]); Likewise. Thanks. ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2] branch: segfault fixes and validation 2013-02-21 17:47 ` Junio C Hamano @ 2013-02-22 11:47 ` Nguyễn Thái Ngọc Duy 2013-02-22 20:27 ` Junio C Hamano 0 siblings, 1 reply; 10+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2013-02-22 11:47 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Per Cederqvist, Nguyễn Thái Ngọc Duy branch_get() can return NULL (so far on detached HEAD only) but some code paths in builtin/branch.c cannot deal with that and cause segfaults. While at there, make sure we bail out when the user gives 2 or more arguments, but we only process the first one and silently ignore the rest. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- On Fri, Feb 22, 2013 at 12:47 AM, Junio C Hamano <gitster@pobox.com> wrote: > Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > >> branch_get() can return NULL (so far on detached HEAD only)... > > Do you anticipate any other cases where the API call should validly > return NULL? No. But I do not see any guarantee that it may never do that in future either. Which is why I was deliberately vague with "could not figure out...". But you also correctly observed my laziness there. So how about this? It makes a special case for HEAD but not insist on detached HEAD as the only cause. builtin/branch.c | 24 ++++++++++++++++++++++++ t/t3200-branch.sh | 21 +++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/builtin/branch.c b/builtin/branch.c index 6371bf9..82ed337 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -889,6 +889,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix) } else if (new_upstream) { struct branch *branch = branch_get(argv[0]); + if (argc > 1) + die(_("too many branches to set new upstream")); + + if (!branch) { + if (!argc || !strcmp(argv[0], "HEAD")) + die(_("HEAD does not point to any branch. Is it detached?")); + die(_("no such branch '%s'"), argv[0]); + } + if (!ref_exists(branch->refname)) die(_("branch '%s' does not exist"), branch->name); @@ -901,6 +910,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix) struct branch *branch = branch_get(argv[0]); struct strbuf buf = STRBUF_INIT; + if (argc > 1) + die(_("too many branches to unset upstream")); + + if (!branch) { + if (!argc || !strcmp(argv[0], "HEAD")) + die(_("HEAD does not point to any branch. Is it detached?")); + die(_("no such branch '%s'"), argv[0]); + } + if (!branch_has_merge_config(branch)) { die(_("Branch '%s' has no upstream information"), branch->name); } @@ -916,6 +934,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix) int branch_existed = 0, remote_tracking = 0; struct strbuf buf = STRBUF_INIT; + if (!strcmp(argv[0], "HEAD")) + die(_("it does not make sense to create 'HEAD' manually")); + + if (!branch) + die(_("no such branch '%s'"), argv[0]); + if (kinds != REF_LOCAL_BRANCH) die(_("-a and -r options to 'git branch' do not make sense with a branch name")); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index f3e0e4a..12f1e4a 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -42,6 +42,10 @@ test_expect_success \ 'git branch a/b/c should create a branch' \ 'git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c' +test_expect_success \ + 'git branch HEAD should fail' \ + 'test_must_fail git branch HEAD' + cat >expect <<EOF $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master EOF @@ -388,6 +392,14 @@ test_expect_success \ 'git tag foobar && test_must_fail git branch --track my11 foobar' +test_expect_success '--set-upstream-to fails on multiple branches' \ + 'test_must_fail git branch --set-upstream-to master a b c' + +test_expect_success '--set-upstream-to fails on detached HEAD' \ + 'git checkout HEAD^{} && + test_must_fail git branch --set-upstream-to master && + git checkout -' + test_expect_success 'use --set-upstream-to modify HEAD' \ 'test_config branch.master.remote foo && test_config branch.master.merge foo && @@ -417,6 +429,15 @@ test_expect_success 'test --unset-upstream on HEAD' \ test_must_fail git branch --unset-upstream ' +test_expect_success '--unset-upstream should fail on multiple branches' \ + 'test_must_fail git branch --unset-upstream a b c' + +test_expect_success '--unset-upstream should fail on detached HEAD' \ + 'git checkout HEAD^{} && + test_must_fail git branch --unset-upstream && + git checkout - +' + test_expect_success 'test --unset-upstream on a particular branch' \ 'git branch my15 git branch --set-upstream-to master my14 && -- 1.8.1.2.536.gf441e6d ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2] branch: segfault fixes and validation 2013-02-22 11:47 ` [PATCH v2] " Nguyễn Thái Ngọc Duy @ 2013-02-22 20:27 ` Junio C Hamano 2013-02-23 12:22 ` [PATCH v3] " Nguyễn Thái Ngọc Duy 0 siblings, 1 reply; 10+ messages in thread From: Junio C Hamano @ 2013-02-22 20:27 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git, Per Cederqvist Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > branch_get() can return NULL (so far on detached HEAD only) but some > code paths in builtin/branch.c cannot deal with that and cause > segfaults. While at there, make sure we bail out when the user gives 2 > or more arguments, but we only process the first one and silently > ignore the rest. Explain "2 or more arguments" in what context, perhaps? Otherwise it makes it sound as if "git branch foo bar baz" is covered with this patch, no? > Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> > --- > On Fri, Feb 22, 2013 at 12:47 AM, Junio C Hamano <gitster@pobox.com> wrote: > > Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > > > >> branch_get() can return NULL (so far on detached HEAD only)... > > > > Do you anticipate any other cases where the API call should validly > > return NULL? > > No. But I do not see any guarantee that it may never do that in > future either. Which is why I was deliberately vague with "could not > figure out...". But you also correctly observed my laziness there. So > how about this? It makes a special case for HEAD but not insist on > detached HEAD as the only cause. Sure. It looks better. What you can do is to have a single helper function that can explain why branch_get() returned NULL (or extend branch_get() to serve that purpose as well); then you do not have to duplicate the logic twice on the caller's side (and there may be other callers that want to do the same). > diff --git a/builtin/branch.c b/builtin/branch.c > index 6371bf9..82ed337 100644 > --- a/builtin/branch.c > +++ b/builtin/branch.c > @@ -889,6 +889,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix) > } else if (new_upstream) { > struct branch *branch = branch_get(argv[0]); > > + if (argc > 1) > + die(_("too many branches to set new upstream")); > + > + if (!branch) { > + if (!argc || !strcmp(argv[0], "HEAD")) > + die(_("HEAD does not point to any branch. Is it detached?")); > + die(_("no such branch '%s'"), argv[0]); > + } > + > if (!ref_exists(branch->refname)) > die(_("branch '%s' does not exist"), branch->name); The latter part of the new code triggers when branch_get() returns NULL while doing "git branch --set-upstream-to=X [Y]". When "Y" is string "HEAD" or missing, the first die() is triggered and says a funny thing. If HEAD does not point to any branch, by definition it is detached. The user may say "Yes, I know it is detached." but the message does not help the user to figure out what to do next. Instead of asking "Is it detached?", perhaps we can say something like "You told me to set the upstream of HEAD to branch X, but " in front? At least, that will be a better explanation for the reason why the operation is failing. The existing test might be wrong, by the way. Your HEAD may point at a branch Y but you may not have any commit on it yet, and you may want to allow setting the upstream of that to-be-born branch to another branch X with "branch --set-upstream-to=X [Y|HEAD]". While I think it is insane to do anything before creating the first commit on your current branch (or using "checkout --orphan" in general) and it may not be worth our time to babysit users who do so, but the following sequence may feel natural to them: git checkout --orphan X git branch --set-upstream-to=master ... perhaps create an initial commit, perhaps not ... git merge @{upstream} For that to work sanely, perhaps the pattern branch = branch_get(); if (!branch) die due to no branch; if (!ref_exists(branch->refname)) die due to typo in branch name may need to be fixed globally, replacing ref_exists(branch->refname) with branch_exists(branch) that returns true if branch->refname is an existing ref, or the branch in question was obtained by checking with current_branch (in remote.c), or something like that. ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3] branch: segfault fixes and validation 2013-02-22 20:27 ` Junio C Hamano @ 2013-02-23 12:22 ` Nguyễn Thái Ngọc Duy 2013-02-23 20:01 ` Junio C Hamano 0 siblings, 1 reply; 10+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2013-02-23 12:22 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Per Cederqvist, Nguyễn Thái Ngọc Duy branch_get() can return NULL (so far on detached HEAD only) but some code paths in builtin/branch.c cannot deal with that and cause segfaults. While at there, make sure to bail out when the user gives 2 or more branches with --set-upstream-to or --unset-upstream, where only the first branch is processed and the rest silently dropped. Reported-by: Per Cederqvist <cederp@opera.com> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- On Sat, Feb 23, 2013 at 3:27 AM, Junio C Hamano <gitster@pobox.com> wrote: > Instead of asking "Is it detached?", perhaps we can say something > like "You told me to set the upstream of HEAD to branch X, but " in > front? At least, that will be a better explanation for the reason > why the operation is failing. Fixed. > What you can do is to have a single helper function that can explain > why branch_get() returned NULL (or extend branch_get() to serve that > purpose as well); then you do not have to duplicate the logic twice > on the caller's side (and there may be other callers that want to do > the same). The explanation mentions about the failed operation, which makes a helper less useful. We could still do the helper, but it may lead to i18n legos. So no helper in this version. > The existing test might be wrong, by the way. Your HEAD may point > at a branch Y but you may not have any commit on it yet, and you may > want to allow setting the upstream of that to-be-born branch to > another branch X with "branch --set-upstream-to=X [Y|HEAD]". It sounds complicated. I think we can revisit it when a user actually complains about it. builtin/branch.c | 27 +++++++++++++++++++++++++++ t/t3200-branch.sh | 21 +++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/builtin/branch.c b/builtin/branch.c index 6371bf9..00d17d2 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -889,6 +889,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix) } else if (new_upstream) { struct branch *branch = branch_get(argv[0]); + if (argc > 1) + die(_("too many branches to set new upstream")); + + if (!branch) { + if (!argc || !strcmp(argv[0], "HEAD")) + die(_("could not set upstream of HEAD to %s when " + "it does not point to any branch."), + new_upstream); + die(_("no such branch '%s'"), argv[0]); + } + if (!ref_exists(branch->refname)) die(_("branch '%s' does not exist"), branch->name); @@ -901,6 +912,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix) struct branch *branch = branch_get(argv[0]); struct strbuf buf = STRBUF_INIT; + if (argc > 1) + die(_("too many branches to unset upstream")); + + if (!branch) { + if (!argc || !strcmp(argv[0], "HEAD")) + die(_("could not unset upstream of HEAD when " + "it does not point to any branch.")); + die(_("no such branch '%s'"), argv[0]); + } + if (!branch_has_merge_config(branch)) { die(_("Branch '%s' has no upstream information"), branch->name); } @@ -916,6 +937,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix) int branch_existed = 0, remote_tracking = 0; struct strbuf buf = STRBUF_INIT; + if (!strcmp(argv[0], "HEAD")) + die(_("it does not make sense to create 'HEAD' manually")); + + if (!branch) + die(_("no such branch '%s'"), argv[0]); + if (kinds != REF_LOCAL_BRANCH) die(_("-a and -r options to 'git branch' do not make sense with a branch name")); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index f3e0e4a..12f1e4a 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -42,6 +42,10 @@ test_expect_success \ 'git branch a/b/c should create a branch' \ 'git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c' +test_expect_success \ + 'git branch HEAD should fail' \ + 'test_must_fail git branch HEAD' + cat >expect <<EOF $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master EOF @@ -388,6 +392,14 @@ test_expect_success \ 'git tag foobar && test_must_fail git branch --track my11 foobar' +test_expect_success '--set-upstream-to fails on multiple branches' \ + 'test_must_fail git branch --set-upstream-to master a b c' + +test_expect_success '--set-upstream-to fails on detached HEAD' \ + 'git checkout HEAD^{} && + test_must_fail git branch --set-upstream-to master && + git checkout -' + test_expect_success 'use --set-upstream-to modify HEAD' \ 'test_config branch.master.remote foo && test_config branch.master.merge foo && @@ -417,6 +429,15 @@ test_expect_success 'test --unset-upstream on HEAD' \ test_must_fail git branch --unset-upstream ' +test_expect_success '--unset-upstream should fail on multiple branches' \ + 'test_must_fail git branch --unset-upstream a b c' + +test_expect_success '--unset-upstream should fail on detached HEAD' \ + 'git checkout HEAD^{} && + test_must_fail git branch --unset-upstream && + git checkout - +' + test_expect_success 'test --unset-upstream on a particular branch' \ 'git branch my15 git branch --set-upstream-to master my14 && -- 1.8.1.2.536.gf441e6d ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v3] branch: segfault fixes and validation 2013-02-23 12:22 ` [PATCH v3] " Nguyễn Thái Ngọc Duy @ 2013-02-23 20:01 ` Junio C Hamano 0 siblings, 0 replies; 10+ messages in thread From: Junio C Hamano @ 2013-02-23 20:01 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git, Per Cederqvist Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > > What you can do is to have a single helper function that can explain > > why branch_get() returned NULL (or extend branch_get() to serve that > > purpose as well); then you do not have to duplicate the logic twice > > on the caller's side (and there may be other callers that want to do > > the same). > > The explanation mentions about the failed operation, which makes a > helper less useful. We could still do the helper, but it may lead to > i18n legos. So no helper in this version. Who said explanation has to be conveyed in human language to the caller of the helper? Since there are only two possible cases, at least for now, and I do not think there will be countless many in the future, for the branch_get() function to fail, you can still do: int explain_error; struct branch *branch = branch_get(argv[0], &explain_error); switch (explain_error) { case DEFAULT_HEAD_DETACHED: case GIVEN_HEAD_DETACHED: die(_("could not make %s upstream of the current branch " "because you do not have one"), new_upstream); break; default: break; } and we could even fold the !ref_exists() check that is there for typo-detection purposes into the framework, e.g. case GIVEN_BRANCH_DOES_NOT_EXIST: die(_("you told me to make %s upstream of %s " "but the latter does not exist yet"), new_upstream, argv[0]); if we wanted to preserve what the current test does, no? > > The existing test might be wrong, by the way. Your HEAD may point > > at a branch Y but you may not have any commit on it yet, and you may > > want to allow setting the upstream of that to-be-born branch to > > another branch X with "branch --set-upstream-to=X [Y|HEAD]". > > It sounds complicated. I think we can revisit it when a user actually > complains about it. Yeah, will replace the previous one and queue this version. Thanks. ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2013-02-23 20:02 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-02-21 12:27 "git branch HEAD" dumps core when on detached head (NULL pointer dereference) Per Cederqvist 2013-02-21 12:50 ` Duy Nguyen 2013-02-21 13:24 ` Per Cederqvist 2013-02-21 13:32 ` Duy Nguyen 2013-02-21 14:18 ` [PATCH] branch: segfault fixes and validation Nguyễn Thái Ngọc Duy 2013-02-21 17:47 ` Junio C Hamano 2013-02-22 11:47 ` [PATCH v2] " Nguyễn Thái Ngọc Duy 2013-02-22 20:27 ` Junio C Hamano 2013-02-23 12:22 ` [PATCH v3] " Nguyễn Thái Ngọc Duy 2013-02-23 20:01 ` Junio C Hamano
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).