{"thread":{"id":"5387","subject":"[PATCH] Rewrite branch in C and make it a builtin.","startedAt":"2006-08-26T03:15:27Z","lastAt":"2006-09-02T17:39:00Z","messageCount":3,"participants":["Kristian HÃ¸gsberg","Shawn Pearce","Kristian Høgsberg"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"25916","messageId":"1156562127979-git-send-email-krh@redhat.com","threadId":"5387","inReplyTo":null,"subject":"[PATCH] Rewrite branch in C and make it a builtin.","fromName":"Kristian HÃ¸gsberg","fromEmail":"krh@redhat.com","sentAt":"2006-08-26T03:15:27Z","receivedAt":"2006-08-26T03:15:27Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"From: =?utf-8?q?Kristian_H=C3=B8gsberg?= <krh@redhat.com>\n\nA more or less straight port to C of the shell script version.\n\nSigned-off-by: Kristian HÃ¸gsberg <krh@redhat.com>\n---\n Makefile         |    3 -\n builtin-branch.c |  227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n builtin.h        |    1 \n git-branch.sh    |  130 -------------------------------\n git.c            |    1 \n 5 files changed, 231 insertions(+), 131 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 23cd8a0..adf043e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -149,7 +149,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powe\n ### --- END CONFIGURATION SECTION ---\n \n SCRIPT_SH = \\\n-\tgit-bisect.sh git-branch.sh git-checkout.sh \\\n+\tgit-bisect.sh git-checkout.sh \\\n \tgit-cherry.sh git-clean.sh git-clone.sh git-commit.sh \\\n \tgit-fetch.sh \\\n \tgit-ls-remote.sh \\\n@@ -253,6 +253,7 @@ LIB_OBJS = \\\n BUILTIN_OBJS = \\\n \tbuiltin-add.o \\\n \tbuiltin-apply.o \\\n+\tbuiltin-branch.o \\\n \tbuiltin-cat-file.o \\\n \tbuiltin-checkout-index.o \\\n \tbuiltin-check-ref-format.o \\\ndiff --git a/builtin-branch.c b/builtin-branch.c\nnew file mode 100644\nindex 0000000..c8d0275\n--- /dev/null\n+++ b/builtin-branch.c\n@@ -0,0 +1,227 @@\n+/*\n+ * Builtin \"git branch\"\n+ *\n+ * Copyright (c) 2006 Kristian HÃ¸gsberg <krh@redhat.com>\n+ * Based on git-branch.sh by Junio C Hamano.\n+ */\n+\n+#include \"cache.h\"\n+#include \"refs.h\"\n+#include \"commit.h\"\n+#include \"builtin.h\"\n+\n+static const char builtin_branch_usage[] =\n+\"git-branch [(-d | -D) <branchname>] | [[-f] <branchname> [<start-point>]] | -r\";\n+\n+\n+static const char *head;\n+static unsigned char head_sha1[20];\n+\n+static int in_merge_bases(const unsigned char *sha1,\n+\t\t\t  struct commit *rev1,\n+\t\t\t  struct commit *rev2)\n+{\n+\tstruct commit_list *bases, *b;\n+\tint ret = 0;\n+\n+\tbases = get_merge_bases(rev1, rev2, 1);\n+\tfor (b = bases; b; b = b->next) {\n+\t\tif (!hashcmp(sha1, b->item->object.sha1)) {\n+\t\t\tret = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tfree_commit_list(bases);\n+\treturn ret;\n+}\n+\n+static void delete_branches(int argc, const char **argv, int force)\n+{\n+\tstruct commit *rev, *head_rev;\n+\tunsigned char sha1[20];\n+\tconst char *name, *reflog;\n+\tint i;\n+\n+\thead_rev = lookup_commit_reference(head_sha1);\n+\tfor (i = 0; i < argc; i++) {\n+\t\tif (!strcmp(head, argv[i]))\n+\t\t\tdie(\"Cannot delete the branch you are currently on.\");\n+\n+\t\tname = git_path(\"refs/heads/%s\", argv[i]);\n+\t\tif (!resolve_ref(name, sha1, 1))\n+\t\t\tdie(\"Branch '%s' not found.\", argv[i]);\n+\n+\t\trev = lookup_commit_reference(sha1);\n+\t\tif (!rev || !head_rev)\n+\t\t\tdie(\"Couldn't look up commit objects.\");\n+\n+\t\t/* This checks whether the merge bases of branch and\n+\t\t * HEAD contains branch -- which means that the HEAD\n+\t\t * contains everything in both.\n+\t\t */\n+\n+\t\tif (!force &&\n+\t\t    !in_merge_bases(sha1, rev, head_rev)) {\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"The branch '%s' is not a strict subset of your current HEAD.\\n\"\n+\t\t\t\t\"If you are sure you want to delete it, run 'git branch -D %s'.\\n\",\n+\t\t\t\targv[i], argv[i]);\n+\t\t\texit(1);\n+\t\t}\n+\n+\t\tunlink(name);\n+\n+\t\t/* Unlink reflog if it exists. */\n+\t\treflog = git_path(\"logs/refs/heads/%s\", argv[i]);\n+\t\tunlink(reflog);\n+\n+\t\tprintf(\"Deleted branch %s.\\n\", argv[i]);\n+\t}\n+}\n+\n+static int ref_index, ref_alloc;\n+static char **ref_list;\n+\n+static int append_ref(const char *refname, const unsigned char *sha1)\n+{\n+\tif (ref_index >= ref_alloc) {\n+\t\tref_alloc = ref_alloc > 0 ? ref_alloc * 2 : 16;\n+\t\tref_list = xrealloc(ref_list, ref_alloc * sizeof(char *));\n+\t}\n+\n+\tref_list[ref_index++] = strdup(refname);\n+\n+\treturn 0;\n+}\n+\n+static int ref_cmp(const void *r1, const void *r2)\n+{\n+\treturn strcmp(*(char **)r1, *(char **)r2);\n+}\n+\n+static void print_ref_list(int remote_only)\n+{\n+\tint i;\n+\n+\tif (remote_only)\n+\t\tfor_each_remote_ref(append_ref);\n+\telse\n+\t\tfor_each_branch_ref(append_ref);\n+\n+\tqsort(ref_list, ref_index, sizeof(char *), ref_cmp);\n+\n+\tfor (i = 0; i < ref_index; i++) {\n+\t\tif (!strcmp(ref_list[i], head))\n+\t\t\tprintf(\"* %s\\n\", ref_list[i]);\n+\t\telse\n+\t\t\tprintf(\"  %s\\n\", ref_list[i]);\n+\t}\n+}\n+\n+static void create_reflog(struct ref_lock *lock)\n+{\n+\tstruct stat stbuf;\n+\tint fd;\n+\n+\tif (!stat(lock->log_file, &stbuf) && S_ISREG(stbuf.st_mode))\n+\t\treturn;\n+\tif (safe_create_leading_directories(lock->log_file) < 0)\n+\t\tdie(\"Unable to create directory for %s.\", lock->log_file);\n+\tfd = open(lock->log_file, O_CREAT | O_TRUNC | O_WRONLY, 0666);\n+\tif (fd < 0)\n+\t\tdie(\"Unable to create ref log %s: %s.\",\n+\t\t    lock->log_file, strerror(errno));\n+\tclose(fd);\n+}\n+\n+static void create_branch(const char *name, const char *start,\n+\t\t\t  int force, int reflog)\n+{\n+\tstruct ref_lock *lock;\n+\tunsigned char sha1[20];\n+\tchar ref[PATH_MAX], msg[PATH_MAX + 20];\n+\n+\tsnprintf(ref, sizeof ref, \"refs/heads/%s\", name);\n+\tif (check_ref_format(ref))\n+\t\tdie(\"'%s' is not a valid branch name.\", name);\n+\n+\tif (resolve_ref(git_path(ref), sha1, 1)) {\n+\t\tif (!force)\n+\t\t\tdie(\"A branch named '%s' already exists.\", name);\n+\t\telse if (!strcmp(head, name))\n+\t\t\tdie(\"Cannot force update the current branch.\");\n+\t}\n+\n+\tif (get_sha1(start, sha1))\n+\t\tdie(\"Not a valid branch point: '%s'.\", start);\n+\n+\tlock = lock_any_ref_for_update(ref, NULL, 0);\n+\tif (!lock)\n+\t\tdie(\"Failed to lock ref for update: %s.\", strerror(errno));\n+\tif (reflog)\n+\t\tcreate_reflog(lock);\n+\tsnprintf(msg, sizeof msg, \"branch: Created from %s\", start);\n+\tif (write_ref_sha1(lock, sha1, msg) < 0)\n+\t\tdie(\"Failed to write ref: %s.\", strerror(errno));\n+}\n+\n+int cmd_branch(int argc, const char **argv, const char *prefix)\n+{\n+\tint delete = 0, force_delete = 0, force_create = 0, remote_only = 0;\n+\tint reflog = 0;\n+\tint i, prefix_length;\n+\tconst char *p;\n+\n+\tgit_config(git_default_config);\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\n+\t\tif (arg[0] != '-')\n+\t\t\tbreak;\n+\t\tif (!strcmp(arg, \"--\")) {\n+\t\t\ti++;\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-d\")) {\n+\t\t\tdelete = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-D\")) {\n+\t\t\tdelete = 1;\n+\t\t\tforce_delete = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-f\")) {\n+\t\t\tforce_create = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-r\")) {\n+\t\t\tremote_only = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-l\")) {\n+\t\t\treflog = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tusage(builtin_branch_usage);\n+\t}\n+\n+\tprefix_length = strlen(git_path(\"refs/heads/\"));\n+\tp = resolve_ref(git_path(\"HEAD\"), head_sha1, 0);\n+\tif (!p)\n+\t\tdie(\"Failed to resolve HEAD as a valid ref.\");\n+\thead = strdup(p + prefix_length);\n+\n+\tif (delete)\n+\t\tdelete_branches(argc - i, argv + i, force_delete);\n+\telse if (i == argc)\n+\t\tprint_ref_list(remote_only);\n+\telse if (argc - i == 1)\n+\t\tcreate_branch(argv[i], head, force_create, reflog);\n+\telse\n+\t\tcreate_branch(argv[i], argv[i + 1], force_create, reflog);\n+\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex ade58c4..eb28986 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -15,6 +15,7 @@ extern int write_tree(unsigned char *sha\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n extern int cmd_apply(int argc, const char **argv, const char *prefix);\n+extern int cmd_branch(int argc, const char **argv, const char *prefix);\n extern int cmd_cat_file(int argc, const char **argv, const char *prefix);\n extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);\n extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);\ndiff --git a/git-branch.sh b/git-branch.sh\ndeleted file mode 100755\nindex e0501ec..0000000\n--- a/git-branch.sh\n+++ /dev/null\n@@ -1,130 +0,0 @@\n-#!/bin/sh\n-\n-USAGE='[-l] [(-d | -D) <branchname>] | [[-f] <branchname> [<start-point>]] | -r'\n-LONG_USAGE='If no arguments, show available branches and mark current branch with a star.\n-If one argument, create a new branch <branchname> based off of current HEAD.\n-If two arguments, create a new branch <branchname> based off of <start-point>.'\n-\n-SUBDIRECTORY_OK='Yes'\n-. git-sh-setup\n-\n-headref=$(git-symbolic-ref HEAD | sed -e 's|^refs/heads/||')\n-\n-delete_branch () {\n-    option=\"$1\"\n-    shift\n-    for branch_name\n-    do\n-\tcase \",$headref,\" in\n-\t\",$branch_name,\")\n-\t    die \"Cannot delete the branch you are on.\" ;;\n-\t,,)\n-\t    die \"What branch are you on anyway?\" ;;\n-\tesac\n-\tbranch=$(cat \"$GIT_DIR/refs/heads/$branch_name\") &&\n-\t    branch=$(git-rev-parse --verify \"$branch^0\") ||\n-\t\tdie \"Seriously, what branch are you talking about?\"\n-\tcase \"$option\" in\n-\t-D)\n-\t    ;;\n-\t*)\n-\t    mbs=$(git-merge-base -a \"$branch\" HEAD | tr '\\012' ' ')\n-\t    case \" $mbs \" in\n-\t    *' '$branch' '*)\n-\t\t# the merge base of branch and HEAD contains branch --\n-\t\t# which means that the HEAD contains everything in both.\n-\t\t;;\n-\t    *)\n-\t\techo >&2 \"The branch '$branch_name' is not a strict subset of your current HEAD.\n-If you are sure you want to delete it, run 'git branch -D $branch_name'.\"\n-\t\texit 1\n-\t\t;;\n-\t    esac\n-\t    ;;\n-\tesac\n-\trm -f \"$GIT_DIR/logs/refs/heads/$branch_name\"\n-\trm -f \"$GIT_DIR/refs/heads/$branch_name\"\n-\techo \"Deleted branch $branch_name.\"\n-    done\n-    exit 0\n-}\n-\n-ls_remote_branches () {\n-    git-rev-parse --symbolic --all |\n-    sed -ne 's|^refs/\\(remotes/\\)|\\1|p' |\n-    sort\n-}\n-\n-force=\n-create_log=\n-while case \"$#,$1\" in 0,*) break ;; *,-*) ;; *) break ;; esac\n-do\n-\tcase \"$1\" in\n-\t-d | -D)\n-\t\tdelete_branch \"$@\"\n-\t\texit\n-\t\t;;\n-\t-r)\n-\t\tls_remote_branches\n-\t\texit\n-\t\t;;\n-\t-f)\n-\t\tforce=\"$1\"\n-\t\t;;\n-\t-l)\n-\t\tcreate_log=\"yes\"\n-\t\t;;\n-\t--)\n-\t\tshift\n-\t\tbreak\n-\t\t;;\n-\t-*)\n-\t\tusage\n-\t\t;;\n-\tesac\n-\tshift\n-done\n-\n-case \"$#\" in\n-0)\n-\tgit-rev-parse --symbolic --branches |\n-\tsort |\n-\twhile read ref\n-\tdo\n-\t\tif test \"$headref\" = \"$ref\"\n-\t\tthen\n-\t\t\tpfx='*'\n-\t\telse\n-\t\t\tpfx=' '\n-\t\tfi\n-\t\techo \"$pfx $ref\"\n-\tdone\n-\texit 0 ;;\n-1)\n-\thead=HEAD ;;\n-2)\n-\thead=\"$2^0\" ;;\n-esac\n-branchname=\"$1\"\n-\n-rev=$(git-rev-parse --verify \"$head\") || exit\n-\n-git-check-ref-format \"heads/$branchname\" ||\n-\tdie \"we do not like '$branchname' as a branch name.\"\n-\n-if [ -e \"$GIT_DIR/refs/heads/$branchname\" ]\n-then\n-\tif test '' = \"$force\"\n-\tthen\n-\t\tdie \"$branchname already exists.\"\n-\telif test \"$branchname\" = \"$headref\"\n-\tthen\n-\t\tdie \"cannot force-update the current branch.\"\n-\tfi\n-fi\n-if test \"$create_log\" = 'yes'\n-then\n-\tmkdir -p $(dirname \"$GIT_DIR/logs/refs/heads/$branchname\")\n-\ttouch \"$GIT_DIR/logs/refs/heads/$branchname\"\n-fi\n-git update-ref -m \"branch: Created from $head\" \"refs/heads/$branchname\" $rev\ndiff --git a/git.c b/git.c\nindex 930998b..5738cb4 100644\n--- a/git.c\n+++ b/git.c\n@@ -226,6 +226,7 @@ static void handle_internal_command(int \n \t} commands[] = {\n \t\t{ \"add\", cmd_add, RUN_SETUP },\n \t\t{ \"apply\", cmd_apply },\n+\t\t{ \"branch\", cmd_branch, RUN_SETUP },\n \t\t{ \"cat-file\", cmd_cat_file, RUN_SETUP },\n \t\t{ \"checkout-index\", cmd_checkout_index, RUN_SETUP },\n \t\t{ \"check-ref-format\", cmd_check_ref_format },\n-- \n1.4.2.g8153-dirty\n"},{"id":"26198","messageId":"20060902010806.GA24234@spearce.org","threadId":"5387","inReplyTo":"1156562127979-git-send-email-krh@redhat.com","subject":"Re: [PATCH] Rewrite branch in C and make it a builtin.","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2006-09-02T01:08:06Z","receivedAt":"2006-09-02T01:08:06Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Kristian  H??gsberg <krh@redhat.com> wrote:\n> A more or less straight port to C of the shell script version.\n\n[snip]\n> +static void create_reflog(struct ref_lock *lock)\n\nI'm attaching a patch to the reflog code which introduces a new\nforce_log option.  You can use set this after you lock the ref\nbut before writing it, provided that the user supplied -l on\nthe command line.\n\nThis completely replaces the create_reflog function with common code.\n\nI apologize for taking so long to get around to this but I've been\nbusy with other stuff lately.  :-)\n\n\n-- 8> --\nAdd force_log flag to ref_lock to create logs when necessary.\n\nCallers of lock_ref_sha1 or lock_any_ref_for_update may now set\nlck->force_log = 1 if they want to create the associated reflog\nduring write_ref_sha1 if the log is missing.\n\nIf set this will override a false setting of the configuration\nparameter core.logAllRefUpdates and create a missing log, at which\npoint future updates to the same ref would be logged as the log\nis present.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n refs.c |    2 +-\n refs.h |    3 ++-\n 2 files changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex aab14fc..b29e2f4 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -361,7 +361,7 @@ static int log_ref_write(struct ref_lock\n \tchar *logrec;\n \tconst char *committer;\n \n-\tif (log_all_ref_updates) {\n+\tif (log_all_ref_updates || lock->force_log) {\n \t\tif (safe_create_leading_directories(lock->log_file) < 0)\n \t\t\treturn error(\"unable to create directory for %s\",\n \t\t\t\tlock->log_file);\ndiff --git a/refs.h b/refs.h\nindex 553155c..d4798c9 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -7,7 +7,8 @@ struct ref_lock {\n \tstruct lock_file *lk;\n \tunsigned char old_sha1[20];\n \tint lock_fd;\n-\tint force_write;\n+\tint force_write; /* force creating ref if not present */\n+\tint force_log;   /* force creating log if not present */\n };\n \n /*\n-- \n1.4.2.ga2654\n\n\n-- \nVGER BF report: U 0.5\n"},{"id":"26229","messageId":"44F9C1B4.70409@redhat.com","threadId":"5387","inReplyTo":"20060902010806.GA24234@spearce.org","subject":"Re: [PATCH] Rewrite branch in C and make it a builtin.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2006-09-02T17:39:00Z","receivedAt":"2006-09-02T17:39:00Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"Shawn Pearce wrote:\n> Kristian  H??gsberg <krh@redhat.com> wrote:\n>> A more or less straight port to C of the shell script version.\n> \n> [snip]\n>> +static void create_reflog(struct ref_lock *lock)\n> \n> I'm attaching a patch to the reflog code which introduces a new\n> force_log option.  You can use set this after you lock the ref\n> but before writing it, provided that the user supplied -l on\n> the command line.\n> \n> This completely replaces the create_reflog function with common code.\n\nYeah, that should work, I'll send out a new version that uses the new flag.\n\n> I apologize for taking so long to get around to this but I've been\n> busy with other stuff lately.  :-)\n\nNo problem at all, I'm working on this on and off anyway :)\n\nKristian\n\n\n-- \nVGER BF report: U 0.499995\n"}]}