branch: segfault fixes and validation

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> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Feb 23, 2013 at 19:22 UTC 8efb8899cfe866dddb3659b9e0a94232161db65e
2 files changed +48
builtin/branch.c
+27
@@ -889,6 +889,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
889 } else if (new_upstream) {
890 struct branch *branch = branch_get(argv[0]);
891
892 + if (argc > 1)
893 + die(_("too many branches to set new upstream"));
894 +
895 + if (!branch) {
896 + if (!argc || !strcmp(argv[0], "HEAD"))
897 + die(_("could not set upstream of HEAD to %s when "
898 + "it does not point to any branch."),
899 + new_upstream);
900 + die(_("no such branch '%s'"), argv[0]);
901 + }
902 +
903 if (!ref_exists(branch->refname))
904 die(_("branch '%s' does not exist"), branch->name);
905
@@ -901,6 +912,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
912 struct branch *branch = branch_get(argv[0]);
913 struct strbuf buf = STRBUF_INIT;
914
915 + if (argc > 1)
916 + die(_("too many branches to unset upstream"));
917 +
918 + if (!branch) {
919 + if (!argc || !strcmp(argv[0], "HEAD"))
920 + die(_("could not unset upstream of HEAD when "
921 + "it does not point to any branch."));
922 + die(_("no such branch '%s'"), argv[0]);
923 + }
924 +
925 if (!branch_has_merge_config(branch)) {
926 die(_("Branch '%s' has no upstream information"), branch->name);
927 }
@@ -916,6 +937,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
937 int branch_existed = 0, remote_tracking = 0;
938 struct strbuf buf = STRBUF_INIT;
939
940 + if (!strcmp(argv[0], "HEAD"))
941 + die(_("it does not make sense to create 'HEAD' manually"));
942 +
943 + if (!branch)
944 + die(_("no such branch '%s'"), argv[0]);
945 +
946 if (kinds != REF_LOCAL_BRANCH)
947 die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
948
t/t3200-branch.sh
+21
@@ -42,6 +42,10 @@ test_expect_success \
42 'git branch a/b/c should create a branch' \
43 'git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c'
44
45 +test_expect_success \
46 + 'git branch HEAD should fail' \
47 + 'test_must_fail git branch HEAD'
48 +
49 cat >expect <<EOF
50 $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
51 EOF
@@ -388,6 +392,14 @@ test_expect_success \
392 'git tag foobar &&
393 test_must_fail git branch --track my11 foobar'
394
395 +test_expect_success '--set-upstream-to fails on multiple branches' \
396 + 'test_must_fail git branch --set-upstream-to master a b c'
397 +
398 +test_expect_success '--set-upstream-to fails on detached HEAD' \
399 + 'git checkout HEAD^{} &&
400 + test_must_fail git branch --set-upstream-to master &&
401 + git checkout -'
402 +
403 test_expect_success 'use --set-upstream-to modify HEAD' \
404 'test_config branch.master.remote foo &&
405 test_config branch.master.merge foo &&
@@ -417,6 +429,15 @@ test_expect_success 'test --unset-upstream on HEAD' \
429 test_must_fail git branch --unset-upstream
430 '
431
432 +test_expect_success '--unset-upstream should fail on multiple branches' \
433 + 'test_must_fail git branch --unset-upstream a b c'
434 +
435 +test_expect_success '--unset-upstream should fail on detached HEAD' \
436 + 'git checkout HEAD^{} &&
437 + test_must_fail git branch --unset-upstream &&
438 + git checkout -
439 +'
440 +
441 test_expect_success 'test --unset-upstream on a particular branch' \
442 'git branch my15
443 git branch --set-upstream-to master my14 &&