Make git-branch a builtin
This replaces git-branch.sh with builtin-branch.c The changes is basically a patch from Kristian Høgsberg, updated to apply onto current 'next' Signed-off-by: Lars Hjemli <hjemli@gmail.com> Signed-off-by: Junio C Hamano <junkio@cox.net>maint
							parent
							
								
									3b463c3f02
								
							
						
					
					
						commit
						c31820c26b
					
				
							
								
								
									
										3
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										3
									
								
								Makefile
								
								
								
								
							|  | @ -156,7 +156,7 @@ BASIC_CFLAGS = | |||
| BASIC_LDFLAGS = | ||||
|  | ||||
| SCRIPT_SH = \ | ||||
| 	git-bisect.sh git-branch.sh git-checkout.sh \ | ||||
| 	git-bisect.sh git-checkout.sh \ | ||||
| 	git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \ | ||||
| 	git-fetch.sh \ | ||||
| 	git-ls-remote.sh \ | ||||
|  | @ -267,6 +267,7 @@ BUILTIN_OBJS = \ | |||
| 	builtin-add.o \ | ||||
| 	builtin-apply.o \ | ||||
| 	builtin-archive.o \ | ||||
| 	builtin-branch.o \ | ||||
| 	builtin-cat-file.o \ | ||||
| 	builtin-checkout-index.o \ | ||||
| 	builtin-check-ref-format.o \ | ||||
|  |  | |||
|  | @ -0,0 +1,219 @@ | |||
| /* | ||||
|  * Builtin "git branch" | ||||
|  * | ||||
|  * Copyright (c) 2006 Kristian Høgsberg <krh@redhat.com> | ||||
|  * Based on git-branch.sh by Junio C Hamano. | ||||
|  */ | ||||
|  | ||||
| #include "cache.h" | ||||
| #include "refs.h" | ||||
| #include "commit.h" | ||||
| #include "builtin.h" | ||||
|  | ||||
| static const char builtin_branch_usage[] = | ||||
| "git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | [-r]"; | ||||
|  | ||||
|  | ||||
| static const char *head; | ||||
| static unsigned char head_sha1[20]; | ||||
|  | ||||
| static int in_merge_bases(const unsigned char *sha1, | ||||
| 			  struct commit *rev1, | ||||
| 			  struct commit *rev2) | ||||
| { | ||||
| 	struct commit_list *bases, *b; | ||||
| 	int ret = 0; | ||||
|  | ||||
| 	bases = get_merge_bases(rev1, rev2, 1); | ||||
| 	for (b = bases; b; b = b->next) { | ||||
| 		if (!hashcmp(sha1, b->item->object.sha1)) { | ||||
| 			ret = 1; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	free_commit_list(bases); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static void delete_branches(int argc, const char **argv, int force) | ||||
| { | ||||
| 	struct commit *rev, *head_rev; | ||||
| 	unsigned char sha1[20]; | ||||
| 	char *name; | ||||
| 	int i; | ||||
|  | ||||
| 	head_rev = lookup_commit_reference(head_sha1); | ||||
| 	for (i = 0; i < argc; i++) { | ||||
| 		if (!strcmp(head, argv[i])) | ||||
| 			die("Cannot delete the branch you are currently on."); | ||||
|  | ||||
| 		name = xstrdup(mkpath("refs/heads/%s", argv[i])); | ||||
| 		if (!resolve_ref(name, sha1, 1, NULL)) | ||||
| 			die("Branch '%s' not found.", argv[i]); | ||||
|  | ||||
| 		rev = lookup_commit_reference(sha1); | ||||
| 		if (!rev || !head_rev) | ||||
| 			die("Couldn't look up commit objects."); | ||||
|  | ||||
| 		/* This checks whether the merge bases of branch and | ||||
| 		 * HEAD contains branch -- which means that the HEAD | ||||
| 		 * contains everything in both. | ||||
| 		 */ | ||||
|  | ||||
| 		if (!force && | ||||
| 		    !in_merge_bases(sha1, rev, head_rev)) { | ||||
| 			fprintf(stderr, | ||||
| 				"The branch '%s' is not a strict subset of your current HEAD.\n" | ||||
| 				"If you are sure you want to delete it, run 'git branch -D %s'.\n", | ||||
| 				argv[i], argv[i]); | ||||
| 			exit(1); | ||||
| 		} | ||||
|  | ||||
| 		if (delete_ref(name, sha1)) | ||||
| 			printf("Error deleting branch '%s'\n", argv[i]); | ||||
| 		else | ||||
| 			printf("Deleted branch %s.\n", argv[i]); | ||||
|  | ||||
| 		free(name); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int ref_index, ref_alloc; | ||||
| static char **ref_list; | ||||
|  | ||||
| static int append_ref(const char *refname, const unsigned char *sha1, int flags, | ||||
| 		void *cb_data) | ||||
| { | ||||
| 	if (ref_index >= ref_alloc) { | ||||
| 		ref_alloc = alloc_nr(ref_alloc); | ||||
| 		ref_list = xrealloc(ref_list, ref_alloc * sizeof(char *)); | ||||
| 	} | ||||
|  | ||||
| 	ref_list[ref_index++] = xstrdup(refname); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int ref_cmp(const void *r1, const void *r2) | ||||
| { | ||||
| 	return strcmp(*(char **)r1, *(char **)r2); | ||||
| } | ||||
|  | ||||
| static void print_ref_list(int remote_only) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	if (remote_only) | ||||
| 		for_each_remote_ref(append_ref, NULL); | ||||
| 	else | ||||
| 		for_each_branch_ref(append_ref, NULL); | ||||
|  | ||||
| 	qsort(ref_list, ref_index, sizeof(char *), ref_cmp); | ||||
|  | ||||
| 	for (i = 0; i < ref_index; i++) { | ||||
| 		if (!strcmp(ref_list[i], head)) | ||||
| 			printf("* %s\n", ref_list[i]); | ||||
| 		else | ||||
| 			printf("  %s\n", ref_list[i]); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void create_branch(const char *name, const char *start, | ||||
| 			  int force, int reflog) | ||||
| { | ||||
| 	struct ref_lock *lock; | ||||
| 	struct commit *commit; | ||||
| 	unsigned char sha1[20]; | ||||
| 	char ref[PATH_MAX], msg[PATH_MAX + 20]; | ||||
|  | ||||
| 	snprintf(ref, sizeof ref, "refs/heads/%s", name); | ||||
| 	if (check_ref_format(ref)) | ||||
| 		die("'%s' is not a valid branch name.", name); | ||||
|  | ||||
| 	if (resolve_ref(ref, sha1, 1, NULL)) { | ||||
| 		if (!force) | ||||
| 			die("A branch named '%s' already exists.", name); | ||||
| 		else if (!strcmp(head, name)) | ||||
| 			die("Cannot force update the current branch."); | ||||
| 	} | ||||
|  | ||||
| 	if (get_sha1(start, sha1) || | ||||
| 	    (commit = lookup_commit_reference(sha1)) == NULL) | ||||
| 		die("Not a valid branch point: '%s'.", start); | ||||
| 	hashcpy(sha1, commit->object.sha1); | ||||
|  | ||||
| 	lock = lock_any_ref_for_update(ref, NULL); | ||||
| 	if (!lock) | ||||
| 		die("Failed to lock ref for update: %s.", strerror(errno)); | ||||
|  | ||||
| 	if (reflog) { | ||||
| 		log_all_ref_updates = 1; | ||||
| 		snprintf(msg, sizeof msg, "branch: Created from %s", start); | ||||
| 	} | ||||
|  | ||||
| 	if (write_ref_sha1(lock, sha1, msg) < 0) | ||||
| 		die("Failed to write ref: %s.", strerror(errno)); | ||||
| } | ||||
|  | ||||
| int cmd_branch(int argc, const char **argv, const char *prefix) | ||||
| { | ||||
| 	int delete = 0, force_delete = 0, force_create = 0, remote_only = 0; | ||||
| 	int reflog = 0; | ||||
| 	int i; | ||||
|  | ||||
| 	git_config(git_default_config); | ||||
|  | ||||
| 	for (i = 1; i < argc; i++) { | ||||
| 		const char *arg = argv[i]; | ||||
|  | ||||
| 		if (arg[0] != '-') | ||||
| 			break; | ||||
| 		if (!strcmp(arg, "--")) { | ||||
| 			i++; | ||||
| 			break; | ||||
| 		} | ||||
| 		if (!strcmp(arg, "-d")) { | ||||
| 			delete = 1; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (!strcmp(arg, "-D")) { | ||||
| 			delete = 1; | ||||
| 			force_delete = 1; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (!strcmp(arg, "-f")) { | ||||
| 			force_create = 1; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (!strcmp(arg, "-r")) { | ||||
| 			remote_only = 1; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (!strcmp(arg, "-l")) { | ||||
| 			reflog = 1; | ||||
| 			continue; | ||||
| 		} | ||||
| 		usage(builtin_branch_usage); | ||||
| 	} | ||||
|  | ||||
| 	head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL)); | ||||
| 	if (!head) | ||||
| 		die("Failed to resolve HEAD as a valid ref."); | ||||
| 	if (strncmp(head, "refs/heads/", 11)) | ||||
| 		die("HEAD not found below refs/heads!"); | ||||
| 	head += 11; | ||||
|  | ||||
| 	if (delete) | ||||
| 		delete_branches(argc - i, argv + i, force_delete); | ||||
| 	else if (i == argc) | ||||
| 		print_ref_list(remote_only); | ||||
| 	else if (i == argc - 1) | ||||
| 		create_branch(argv[i], head, force_create, reflog); | ||||
| 	else if (i == argc - 2) | ||||
| 		create_branch(argv[i], argv[i + 1], force_create, reflog); | ||||
| 	else | ||||
| 		usage(builtin_branch_usage); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -15,6 +15,7 @@ extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); | |||
| extern int cmd_add(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_apply(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_archive(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_branch(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_cat_file(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_checkout_index(int argc, const char **argv, const char *prefix); | ||||
| extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix); | ||||
|  |  | |||
							
								
								
									
										131
									
								
								git-branch.sh
								
								
								
								
							
							
						
						
									
										131
									
								
								git-branch.sh
								
								
								
								
							|  | @ -1,131 +0,0 @@ | |||
| #!/bin/sh | ||||
|  | ||||
| USAGE='[-l] [(-d | -D) <branchname>] | [[-f] <branchname> [<start-point>]] | -r' | ||||
| LONG_USAGE='If no arguments, show available branches and mark current branch with a star. | ||||
| If one argument, create a new branch <branchname> based off of current HEAD. | ||||
| If two arguments, create a new branch <branchname> based off of <start-point>.' | ||||
|  | ||||
| SUBDIRECTORY_OK='Yes' | ||||
| . git-sh-setup | ||||
|  | ||||
| headref=$(git-symbolic-ref HEAD | sed -e 's|^refs/heads/||') | ||||
|  | ||||
| delete_branch () { | ||||
|     option="$1" | ||||
|     shift | ||||
|     for branch_name | ||||
|     do | ||||
| 	case ",$headref," in | ||||
| 	",$branch_name,") | ||||
| 	    die "Cannot delete the branch you are on." ;; | ||||
| 	,,) | ||||
| 	    die "What branch are you on anyway?" ;; | ||||
| 	esac | ||||
| 	branch=$(git-show-ref --verify --hash -- "refs/heads/$branch_name") && | ||||
| 	    branch=$(git-rev-parse --verify "$branch^0") || | ||||
| 		die "Seriously, what branch are you talking about?" | ||||
| 	case "$option" in | ||||
| 	-D) | ||||
| 	    ;; | ||||
| 	*) | ||||
| 	    mbs=$(git-merge-base -a "$branch" HEAD | tr '\012' ' ') | ||||
| 	    case " $mbs " in | ||||
| 	    *' '$branch' '*) | ||||
| 		# the merge base of branch and HEAD contains branch -- | ||||
| 		# which means that the HEAD contains everything in both. | ||||
| 		;; | ||||
| 	    *) | ||||
| 		echo >&2 "The branch '$branch_name' is not a strict subset of your current HEAD. | ||||
| If you are sure you want to delete it, run 'git branch -D $branch_name'." | ||||
| 		exit 1 | ||||
| 		;; | ||||
| 	    esac | ||||
| 	    ;; | ||||
| 	esac | ||||
| 	git update-ref -d "refs/heads/$branch_name" "$branch" | ||||
| 	echo "Deleted branch $branch_name." | ||||
|     done | ||||
|     exit 0 | ||||
| } | ||||
|  | ||||
| ls_remote_branches () { | ||||
|     git-rev-parse --symbolic --all | | ||||
|     sed -ne 's|^refs/\(remotes/\)|\1|p' | | ||||
|     sort | ||||
| } | ||||
|  | ||||
| force= | ||||
| create_log= | ||||
| while case "$#,$1" in 0,*) break ;; *,-*) ;; *) break ;; esac | ||||
| do | ||||
| 	case "$1" in | ||||
| 	-d | -D) | ||||
| 		delete_branch "$@" | ||||
| 		exit | ||||
| 		;; | ||||
| 	-r) | ||||
| 		ls_remote_branches | ||||
| 		exit | ||||
| 		;; | ||||
| 	-f) | ||||
| 		force="$1" | ||||
| 		;; | ||||
| 	-l) | ||||
| 		create_log="yes" | ||||
| 		;; | ||||
| 	--) | ||||
| 		shift | ||||
| 		break | ||||
| 		;; | ||||
| 	-*) | ||||
| 		usage | ||||
| 		;; | ||||
| 	esac | ||||
| 	shift | ||||
| done | ||||
|  | ||||
| case "$#" in | ||||
| 0) | ||||
| 	git-rev-parse --symbolic --branches | | ||||
| 	sort | | ||||
| 	while read ref | ||||
| 	do | ||||
| 		if test "$headref" = "$ref" | ||||
| 		then | ||||
| 			pfx='*' | ||||
| 		else | ||||
| 			pfx=' ' | ||||
| 		fi | ||||
| 		echo "$pfx $ref" | ||||
| 	done | ||||
| 	exit 0 ;; | ||||
| 1) | ||||
| 	head=HEAD ;; | ||||
| 2) | ||||
| 	head="$2^0" ;; | ||||
| esac | ||||
| branchname="$1" | ||||
|  | ||||
| rev=$(git-rev-parse --verify "$head") || exit | ||||
|  | ||||
| git-check-ref-format "heads/$branchname" || | ||||
| 	die "we do not like '$branchname' as a branch name." | ||||
|  | ||||
| prev='' | ||||
| if git-show-ref --verify --quiet -- "refs/heads/$branchname" | ||||
| then | ||||
| 	if test '' = "$force" | ||||
| 	then | ||||
| 		die "$branchname already exists." | ||||
| 	elif test "$branchname" = "$headref" | ||||
| 	then | ||||
| 		die "cannot force-update the current branch." | ||||
| 	fi | ||||
| 	prev=`git rev-parse --verify "refs/heads/$branchname"` | ||||
| fi | ||||
| if test "$create_log" = 'yes' | ||||
| then | ||||
| 	mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$branchname") | ||||
| 	touch "$GIT_DIR/logs/refs/heads/$branchname" | ||||
| fi | ||||
| git update-ref -m "branch: Created from $head" "refs/heads/$branchname" "$rev" "$prev" | ||||
							
								
								
									
										1
									
								
								git.c
								
								
								
								
							
							
						
						
									
										1
									
								
								git.c
								
								
								
								
							|  | @ -221,6 +221,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) | |||
| 		{ "add", cmd_add, RUN_SETUP }, | ||||
| 		{ "apply", cmd_apply }, | ||||
| 		{ "archive", cmd_archive }, | ||||
| 		{ "branch", cmd_branch }, | ||||
| 		{ "cat-file", cmd_cat_file, RUN_SETUP }, | ||||
| 		{ "checkout-index", cmd_checkout_index, RUN_SETUP }, | ||||
| 		{ "check-ref-format", cmd_check_ref_format }, | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Lars Hjemli
						Lars Hjemli