From: Junio C Hamano Date: Fri, 27 Oct 2006 01:48:30 +0000 (-0700) Subject: Merge branch 'jc/reflog' into lj/refs X-Git-Tag: v1.4.4-rc1~44^2 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/2e6d8f181dd77d40a1148549d4f33cdf2877fb19?hp=694500edbd51baef365c588986abe41f01acf0de Merge branch 'jc/reflog' into lj/refs * jc/reflog: sha1_name.c: avoid compilation warnings. ref-log: allow ref@{count} syntax. --- diff --git a/Documentation/config.txt b/Documentation/config.txt index 84e38911ee..232e2a9732 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -71,12 +71,16 @@ core.preferSymlinkRefs:: expect HEAD to be a symbolic link. core.logAllRefUpdates:: - If true, `git-update-ref` will append a line to - "$GIT_DIR/logs/" listing the new SHA1 and the date/time - of the update. If the file does not exist it will be - created automatically. This information can be used to - determine what commit was the tip of a branch "2 days ago". - This value is false by default (no logging). + Updates to a ref is logged to the file + "$GIT_DIR/logs/", by appending the new and old + SHA1, the date/time and the reason of the update, but + only when the file exists. If this configuration + variable is set to true, missing "$GIT_DIR/logs/" + file is automatically created for branch heads. + + This information can be used to determine what commit + was the tip of a branch "2 days ago". This value is + false by default (no automated creation of log files). core.repositoryFormatVersion:: Internal variable identifying the repository format and layout diff --git a/Makefile b/Makefile index e826247cca..be8bf392a2 100644 --- a/Makefile +++ b/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 \ diff --git a/builtin-branch.c b/builtin-branch.c new file mode 100644 index 0000000000..e028a5390f --- /dev/null +++ b/builtin-branch.c @@ -0,0 +1,219 @@ +/* + * Builtin "git branch" + * + * Copyright (c) 2006 Kristian Høgsberg + * 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) | [-l] [-f] [] | [-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; +} diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 23d0d0720e..1087657674 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -2,7 +2,7 @@ #include "refs.h" static const char builtin_pack_refs_usage[] = -"git-pack-refs [--prune]"; +"git-pack-refs [--all] [--prune]"; struct ref_to_prune { struct ref_to_prune *next; @@ -68,6 +68,7 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) { int fd, i; struct pack_refs_cb_data cbdata; + int (*iterate_ref)(each_ref_fn, void *) = for_each_tag_ref; memset(&cbdata, 0, sizeof(cbdata)); @@ -77,6 +78,10 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) cbdata.prune = 1; continue; } + if (!strcmp(arg, "--all")) { + iterate_ref = for_each_ref; + continue; + } /* perhaps other parameters later... */ break; } @@ -88,7 +93,7 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) if (!cbdata.refs_file) die("unable to create ref-pack file structure (%s)", strerror(errno)); - for_each_ref(handle_one_ref, &cbdata); + iterate_ref(handle_one_ref, &cbdata); fflush(cbdata.refs_file); fsync(fd); fclose(cbdata.refs_file); diff --git a/builtin-show-ref.c b/builtin-show-ref.c index f2912e825e..06ec400d7f 100644 --- a/builtin-show-ref.c +++ b/builtin-show-ref.c @@ -3,7 +3,7 @@ #include "object.h" #include "tag.h" -static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash] [--tags] [--heads] [--] [pattern*]"; +static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=]] [--abbrev[=]] [--tags] [--heads] [--] [pattern*]"; static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0, found_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0; diff --git a/builtin.h b/builtin.h index 721b8d8037..db9b369e27 100644 --- a/builtin.h +++ b/builtin.h @@ -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); diff --git a/git-branch.sh b/git-branch.sh deleted file mode 100755 index 4379a07210..0000000000 --- a/git-branch.sh +++ /dev/null @@ -1,131 +0,0 @@ -#!/bin/sh - -USAGE='[-l] [(-d | -D) ] | [[-f] []] | -r' -LONG_USAGE='If no arguments, show available branches and mark current branch with a star. -If one argument, create a new branch based off of current HEAD. -If two arguments, create a new branch based off of .' - -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" diff --git a/git-commit.sh b/git-commit.sh index ee5a165e74..8ac8dcc348 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -466,7 +466,7 @@ then elif test "$use_commit" != "" then git-cat-file commit "$use_commit" | sed -e '1,/^$/d' -elif test -f "$GIT_DIR/MERGE_HEAD" && test -f "$GIT_DIR/MERGE_MSG" +elif test -f "$GIT_DIR/MERGE_MSG" then cat "$GIT_DIR/MERGE_MSG" elif test -f "$GIT_DIR/SQUASH_MSG" @@ -632,7 +632,7 @@ then commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) && rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) && git-update-ref -m "$rloga: $rlogm" HEAD $commit "$current" && - rm -f -- "$GIT_DIR/MERGE_HEAD" && + rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" && if test -f "$NEXT_INDEX" then mv "$NEXT_INDEX" "$THIS_INDEX" diff --git a/git-revert.sh b/git-revert.sh index 2bf35d116c..066e67753e 100755 --- a/git-revert.sh +++ b/git-revert.sh @@ -141,9 +141,18 @@ git-read-tree -m -u --aggressive $base $head $next && result=$(git-write-tree 2>/dev/null) || { echo >&2 "Simple $me fails; trying Automatic $me." git-merge-index -o git-merge-one-file -a || { + mv -f .msg "$GIT_DIR/MERGE_MSG" + { + echo ' +Conflicts: +' + git ls-files --unmerged | + sed -e 's/^[^ ]* / /' | + uniq + } >>"$GIT_DIR/MERGE_MSG" echo >&2 "Automatic $me failed. After resolving the conflicts," echo >&2 "mark the corrected paths with 'git-update-index '" - echo >&2 "and commit with 'git commit -F .msg'" + echo >&2 "and commit the result." case "$me" in cherry-pick) echo >&2 "You may choose to use the following when making" diff --git a/git.c b/git.c index 9108fec808..f197169df2 100644 --- a/git.c +++ b/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 }, diff --git a/refs.c b/refs.c index d7f4aa5d87..f003a0b108 100644 --- a/refs.c +++ b/refs.c @@ -721,7 +721,8 @@ static int log_ref_write(struct ref_lock *lock, char *logrec; const char *committer; - if (log_all_ref_updates) { + if (log_all_ref_updates && + !strncmp(lock->ref_name, "refs/heads/", 11)) { if (safe_create_leading_directories(lock->log_file) < 0) return error("unable to create directory for %s", lock->log_file); @@ -730,10 +731,20 @@ static int log_ref_write(struct ref_lock *lock, logfd = open(lock->log_file, oflags, 0666); if (logfd < 0) { - if (!log_all_ref_updates && errno == ENOENT) + if (!(oflags & O_CREAT) && errno == ENOENT) return 0; - return error("Unable to append to %s: %s", - lock->log_file, strerror(errno)); + + if ((oflags & O_CREAT) && errno == EISDIR) { + if (remove_empty_directories(lock->log_file)) { + return error("There are still logs under '%s'", + lock->log_file); + } + logfd = open(lock->log_file, oflags, 0666); + } + + if (logfd < 0) + return error("Unable to append to %s: %s", + lock->log_file, strerror(errno)); } committer = git_committer_info(1); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 6907cbcd29..acb54b6a07 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -17,13 +17,10 @@ test_expect_success \ git-commit -m "Initial commit." && HEAD=$(git-rev-parse --verify HEAD)' -test_expect_success \ - 'git branch --help should return success now.' \ - 'git-branch --help' - test_expect_failure \ 'git branch --help should not have created a bogus branch' \ - 'test -f .git/refs/heads/--help' + 'git-branch --help /dev/null 2>/dev/null || : + test -f .git/refs/heads/--help' test_expect_success \ 'git branch abc should create a branch' \ @@ -34,7 +31,7 @@ test_expect_success \ 'git-branch a/b/c && test -f .git/refs/heads/a/b/c' cat >expect < 1117150200 +0000 branch: Created from HEAD +0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master EOF test_expect_success \ 'git branch -l d/e/f should create a branch and a log' \ diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh index f31e79c561..b1e9f2eed2 100755 --- a/t/t3210-pack-refs.sh +++ b/t/t3210-pack-refs.sh @@ -11,6 +11,8 @@ semantic is still the same. ' . ./test-lib.sh +echo '[core] logallrefupdates = true' >>.git/config + test_expect_success \ 'prepare a trivial repository' \ 'echo Hello > A && @@ -23,7 +25,7 @@ SHA1= test_expect_success \ 'see if git show-ref works as expected' \ 'git-branch a && - SHA1=$(< .git/refs/heads/a) && + SHA1=`cat .git/refs/heads/a` && echo "$SHA1 refs/heads/a" >expect && git-show-ref a >result && diff expect result' @@ -31,7 +33,7 @@ test_expect_success \ test_expect_success \ 'see if a branch still exists when packed' \ 'git-branch b && - git-pack-refs && + git-pack-refs --all && rm .git/refs/heads/b && echo "$SHA1 refs/heads/b" >expect && git-show-ref b >result && @@ -40,14 +42,14 @@ test_expect_success \ test_expect_failure \ 'git branch c/d should barf if branch c exists' \ 'git-branch c && - git-pack-refs && + git-pack-refs --all && rm .git/refs/heads/c && git-branch c/d' test_expect_success \ 'see if a branch still exists after git pack-refs --prune' \ 'git-branch e && - git-pack-refs --prune && + git-pack-refs --all --prune && echo "$SHA1 refs/heads/e" >expect && git-show-ref e >result && diff expect result' @@ -55,22 +57,22 @@ test_expect_success \ test_expect_failure \ 'see if git pack-refs --prune remove ref files' \ 'git-branch f && - git-pack-refs --prune && + git-pack-refs --all --prune && ls .git/refs/heads/f' test_expect_success \ 'git branch g should work when git branch g/h has been deleted' \ 'git-branch g/h && - git-pack-refs --prune && + git-pack-refs --all --prune && git-branch -d g/h && git-branch g && - git-pack-refs && + git-pack-refs --all && git-branch -d g' test_expect_failure \ 'git branch i/j/k should barf if branch i exists' \ 'git-branch i && - git-pack-refs --prune && + git-pack-refs --all --prune && git-branch i/j/k' test_expect_success \ @@ -90,7 +92,7 @@ test_expect_success \ git-branch -d n/o && git-branch n/o/p && git-branch -d n/op && - git-pack-refs --prune && + git-pack-refs --all --prune && git-branch -d n/o/p && git-branch n'