* [PATCH] Rewrite branch in C and make it a builtin.
@ 2006-08-26 3:15 Kristian Høgsberg
2006-09-02 1:08 ` Shawn Pearce
0 siblings, 1 reply; 3+ messages in thread
From: Kristian Høgsberg @ 2006-08-26 3:15 UTC (permalink / raw)
To: git; +Cc: Kristian Høgsberg
From: =?utf-8?q?Kristian_H=C3=B8gsberg?= <krh@redhat.com>
A more or less straight port to C of the shell script version.
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
---
Makefile | 3 -
builtin-branch.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
builtin.h | 1
git-branch.sh | 130 -------------------------------
git.c | 1
5 files changed, 231 insertions(+), 131 deletions(-)
diff --git a/Makefile b/Makefile
index 23cd8a0..adf043e 100644
--- a/Makefile
+++ b/Makefile
@@ -149,7 +149,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powe
### --- END CONFIGURATION SECTION ---
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 \
@@ -253,6 +253,7 @@ LIB_OBJS = \
BUILTIN_OBJS = \
builtin-add.o \
builtin-apply.o \
+ builtin-branch.o \
builtin-cat-file.o \
builtin-checkout-index.o \
builtin-check-ref-format.o \
diff --git a/builtin-branch.c b/builtin-branch.c
new file mode 100644
index 0000000..c8d0275
--- /dev/null
+++ b/builtin-branch.c
@@ -0,0 +1,227 @@
+/*
+ * 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>] | [[-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];
+ const char *name, *reflog;
+ 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 = git_path("refs/heads/%s", argv[i]);
+ if (!resolve_ref(name, sha1, 1))
+ 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);
+ }
+
+ unlink(name);
+
+ /* Unlink reflog if it exists. */
+ reflog = git_path("logs/refs/heads/%s", argv[i]);
+ unlink(reflog);
+
+ printf("Deleted branch %s.\n", argv[i]);
+ }
+}
+
+static int ref_index, ref_alloc;
+static char **ref_list;
+
+static int append_ref(const char *refname, const unsigned char *sha1)
+{
+ if (ref_index >= ref_alloc) {
+ ref_alloc = ref_alloc > 0 ? ref_alloc * 2 : 16;
+ ref_list = xrealloc(ref_list, ref_alloc * sizeof(char *));
+ }
+
+ ref_list[ref_index++] = strdup(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);
+ else
+ for_each_branch_ref(append_ref);
+
+ 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_reflog(struct ref_lock *lock)
+{
+ struct stat stbuf;
+ int fd;
+
+ if (!stat(lock->log_file, &stbuf) && S_ISREG(stbuf.st_mode))
+ return;
+ if (safe_create_leading_directories(lock->log_file) < 0)
+ die("Unable to create directory for %s.", lock->log_file);
+ fd = open(lock->log_file, O_CREAT | O_TRUNC | O_WRONLY, 0666);
+ if (fd < 0)
+ die("Unable to create ref log %s: %s.",
+ lock->log_file, strerror(errno));
+ close(fd);
+}
+
+static void create_branch(const char *name, const char *start,
+ int force, int reflog)
+{
+ struct ref_lock *lock;
+ 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(git_path(ref), sha1, 1)) {
+ 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))
+ die("Not a valid branch point: '%s'.", start);
+
+ lock = lock_any_ref_for_update(ref, NULL, 0);
+ if (!lock)
+ die("Failed to lock ref for update: %s.", strerror(errno));
+ if (reflog)
+ create_reflog(lock);
+ 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, prefix_length;
+ const char *p;
+
+ 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);
+ }
+
+ prefix_length = strlen(git_path("refs/heads/"));
+ p = resolve_ref(git_path("HEAD"), head_sha1, 0);
+ if (!p)
+ die("Failed to resolve HEAD as a valid ref.");
+ head = strdup(p + prefix_length);
+
+ if (delete)
+ delete_branches(argc - i, argv + i, force_delete);
+ else if (i == argc)
+ print_ref_list(remote_only);
+ else if (argc - i == 1)
+ create_branch(argv[i], head, force_create, reflog);
+ else
+ create_branch(argv[i], argv[i + 1], force_create, reflog);
+
+ return 0;
+}
diff --git a/builtin.h b/builtin.h
index ade58c4..eb28986 100644
--- a/builtin.h
+++ b/builtin.h
@@ -15,6 +15,7 @@ extern int write_tree(unsigned char *sha
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_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);
diff --git a/git-branch.sh b/git-branch.sh
deleted file mode 100755
index e0501ec..0000000
--- a/git-branch.sh
+++ /dev/null
@@ -1,130 +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=$(cat "$GIT_DIR/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
- rm -f "$GIT_DIR/logs/refs/heads/$branch_name"
- rm -f "$GIT_DIR/refs/heads/$branch_name"
- 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."
-
-if [ -e "$GIT_DIR/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
-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
diff --git a/git.c b/git.c
index 930998b..5738cb4 100644
--- a/git.c
+++ b/git.c
@@ -226,6 +226,7 @@ static void handle_internal_command(int
} commands[] = {
{ "add", cmd_add, RUN_SETUP },
{ "apply", cmd_apply },
+ { "branch", cmd_branch, RUN_SETUP },
{ "cat-file", cmd_cat_file, RUN_SETUP },
{ "checkout-index", cmd_checkout_index, RUN_SETUP },
{ "check-ref-format", cmd_check_ref_format },
--
1.4.2.g8153-dirty
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] Rewrite branch in C and make it a builtin.
2006-08-26 3:15 [PATCH] Rewrite branch in C and make it a builtin Kristian Høgsberg
@ 2006-09-02 1:08 ` Shawn Pearce
2006-09-02 17:39 ` Kristian Høgsberg
0 siblings, 1 reply; 3+ messages in thread
From: Shawn Pearce @ 2006-09-02 1:08 UTC (permalink / raw)
To: krh, Junio C Hamano; +Cc: git
Kristian H??gsberg <krh@redhat.com> wrote:
> A more or less straight port to C of the shell script version.
[snip]
> +static void create_reflog(struct ref_lock *lock)
I'm attaching a patch to the reflog code which introduces a new
force_log option. You can use set this after you lock the ref
but before writing it, provided that the user supplied -l on
the command line.
This completely replaces the create_reflog function with common code.
I apologize for taking so long to get around to this but I've been
busy with other stuff lately. :-)
-- 8> --
Add force_log flag to ref_lock to create logs when necessary.
Callers of lock_ref_sha1 or lock_any_ref_for_update may now set
lck->force_log = 1 if they want to create the associated reflog
during write_ref_sha1 if the log is missing.
If set this will override a false setting of the configuration
parameter core.logAllRefUpdates and create a missing log, at which
point future updates to the same ref would be logged as the log
is present.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
refs.c | 2 +-
refs.h | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/refs.c b/refs.c
index aab14fc..b29e2f4 100644
--- a/refs.c
+++ b/refs.c
@@ -361,7 +361,7 @@ static int log_ref_write(struct ref_lock
char *logrec;
const char *committer;
- if (log_all_ref_updates) {
+ if (log_all_ref_updates || lock->force_log) {
if (safe_create_leading_directories(lock->log_file) < 0)
return error("unable to create directory for %s",
lock->log_file);
diff --git a/refs.h b/refs.h
index 553155c..d4798c9 100644
--- a/refs.h
+++ b/refs.h
@@ -7,7 +7,8 @@ struct ref_lock {
struct lock_file *lk;
unsigned char old_sha1[20];
int lock_fd;
- int force_write;
+ int force_write; /* force creating ref if not present */
+ int force_log; /* force creating log if not present */
};
/*
--
1.4.2.ga2654
--
VGER BF report: U 0.5
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] Rewrite branch in C and make it a builtin.
2006-09-02 1:08 ` Shawn Pearce
@ 2006-09-02 17:39 ` Kristian Høgsberg
0 siblings, 0 replies; 3+ messages in thread
From: Kristian Høgsberg @ 2006-09-02 17:39 UTC (permalink / raw)
To: Shawn Pearce; +Cc: Junio C Hamano, git
Shawn Pearce wrote:
> Kristian H??gsberg <krh@redhat.com> wrote:
>> A more or less straight port to C of the shell script version.
>
> [snip]
>> +static void create_reflog(struct ref_lock *lock)
>
> I'm attaching a patch to the reflog code which introduces a new
> force_log option. You can use set this after you lock the ref
> but before writing it, provided that the user supplied -l on
> the command line.
>
> This completely replaces the create_reflog function with common code.
Yeah, that should work, I'll send out a new version that uses the new flag.
> I apologize for taking so long to get around to this but I've been
> busy with other stuff lately. :-)
No problem at all, I'm working on this on and off anyway :)
Kristian
--
VGER BF report: U 0.499995
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2006-09-02 17:40 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-08-26 3:15 [PATCH] Rewrite branch in C and make it a builtin Kristian Høgsberg
2006-09-02 1:08 ` Shawn Pearce
2006-09-02 17:39 ` Kristian Høgsberg
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).