From 6f9a332144cda5f4d7e6e03c37fb17f8ffac1fe3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 21 Sep 2011 20:19:38 -0700 Subject: [PATCH 1/6] branch: add read_branch_desc() helper function This will be used by various callers that make use of the branch description throughout the system, so that if we need to update the implementation the callers do not have to be modified. Signed-off-by: Junio C Hamano --- branch.c | 31 +++++++++++++++++++++++++++++++ branch.h | 5 +++++ 2 files changed, 36 insertions(+) diff --git a/branch.c b/branch.c index fecedd3b46..50088a4190 100644 --- a/branch.c +++ b/branch.c @@ -135,6 +135,37 @@ static int setup_tracking(const char *new_ref, const char *orig_ref, return 0; } +struct branch_desc_cb { + const char *config_name; + const char *value; +}; + +static int read_branch_desc_cb(const char *var, const char *value, void *cb) +{ + struct branch_desc_cb *desc = cb; + if (strcmp(desc->config_name, var)) + return 0; + free((char *)desc->value); + return git_config_string(&desc->value, var, value); +} + +int read_branch_desc(struct strbuf *buf, const char *branch_name) +{ + struct branch_desc_cb cb; + struct strbuf name = STRBUF_INIT; + strbuf_addf(&name, "branch.%s.description", branch_name); + cb.config_name = name.buf; + cb.value = NULL; + if (git_config(read_branch_desc_cb, &cb) < 0) { + strbuf_release(&name); + return -1; + } + if (cb.value) + strbuf_addstr(buf, cb.value); + strbuf_release(&name); + return 0; +} + int validate_new_branchname(const char *name, struct strbuf *ref, int force, int attr_only) { diff --git a/branch.h b/branch.h index 1285158dd4..1493f73722 100644 --- a/branch.h +++ b/branch.h @@ -46,4 +46,9 @@ void remove_branch_state(void); #define BRANCH_CONFIG_VERBOSE 01 extern void install_branch_config(int flag, const char *local, const char *origin, const char *remote); +/* + * Read branch description + */ +extern int read_branch_desc(struct strbuf *, const char *branch_name); + #endif From 739453a3fb74ade725243ac972f02ba1aedabdf6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 21 Sep 2011 20:32:28 -0700 Subject: [PATCH 2/6] format-patch: use branch description in cover letter Use the description for the branch when preparing the cover letter when available. While at it, mark a loosely written codepath that would do a random and useless thing given an unusual input (e.g. "^master HEAD HEAD^"), which we may want to fix someday. Signed-off-by: Junio C Hamano --- builtin/log.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index f5d4930590..e80a925b7b 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -19,6 +19,7 @@ #include "remote.h" #include "string-list.h" #include "parse-options.h" +#include "branch.h" /* Set a default date-time format for git log ("log.date" config variable) */ static const char *default_date_mode = NULL; @@ -746,10 +747,24 @@ static void print_signature(void) printf("-- \n%s\n\n", signature); } +static void add_branch_description(struct strbuf *buf, const char *branch_name) +{ + struct strbuf desc = STRBUF_INIT; + if (!branch_name || !*branch_name) + return; + read_branch_desc(&desc, branch_name); + if (desc.len) { + strbuf_addch(buf, '\n'); + strbuf_add(buf, desc.buf, desc.len); + strbuf_addch(buf, '\n'); + } +} + static void make_cover_letter(struct rev_info *rev, int use_stdout, int numbered, int numbered_files, struct commit *origin, int nr, struct commit **list, struct commit *head, + const char *branch_name, int quiet) { const char *committer; @@ -807,6 +822,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, pp_user_info(&pp, NULL, &sb, committer, encoding); pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte); pp_remainder(&pp, &msg, &sb, 0); + add_branch_description(&sb, branch_name); printf("%s\n", sb.buf); strbuf_release(&sb); @@ -1006,6 +1022,35 @@ static int cc_callback(const struct option *opt, const char *arg, int unset) return 0; } +static char *find_branch_name(struct rev_info *rev) +{ + int i, positive = -1; + unsigned char branch_sha1[20]; + struct strbuf buf = STRBUF_INIT; + const char *branch; + + for (i = 0; i < rev->cmdline.nr; i++) { + if (rev->cmdline.rev[i].flags & UNINTERESTING) + continue; + if (positive < 0) + positive = i; + else + return NULL; + } + if (positive < 0) + return NULL; + strbuf_addf(&buf, "refs/heads/%s", rev->cmdline.rev[positive].name); + branch = resolve_ref(buf.buf, branch_sha1, 1, 0); + if (!branch || + prefixcmp(branch, "refs/heads/") || + hashcmp(rev->cmdline.rev[positive].item->sha1, branch_sha1)) + branch = NULL; + strbuf_release(&buf); + if (branch) + return xstrdup(rev->cmdline.rev[positive].name); + return NULL; +} + int cmd_format_patch(int argc, const char **argv, const char *prefix) { struct commit *commit; @@ -1027,6 +1072,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct strbuf buf = STRBUF_INIT; int use_patch_format = 0; int quiet = 0; + char *branch_name = NULL; const struct option builtin_format_patch_options[] = { { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL, "use [PATCH n/m] even with a single patch", @@ -1217,8 +1263,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) * origin" that prepares what the origin side still * does not have. */ + unsigned char sha1[20]; + const char *ref; + rev.pending.objects[0].item->flags |= UNINTERESTING; add_head_to_pending(&rev); + ref = resolve_ref("HEAD", sha1, 1, NULL); + if (ref && !prefixcmp(ref, "refs/heads/")) + branch_name = xstrdup(ref + strlen("refs/heads/")); + else + branch_name = xstrdup(""); /* no branch */ } /* * Otherwise, it is "format-patch -22 HEAD", and/or @@ -1234,16 +1288,26 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.show_root_diff = 1; if (cover_letter) { - /* remember the range */ + /* + * NEEDSWORK:randomly pick one positive commit to show + * diffstat; this is often the tip and the command + * happens to do the right thing in most cases, but a + * complex command like "--cover-letter a b c ^bottom" + * picks "c" and shows diffstat between bottom..c + * which may not match what the series represents at + * all and totally broken. + */ int i; for (i = 0; i < rev.pending.nr; i++) { struct object *o = rev.pending.objects[i].item; if (!(o->flags & UNINTERESTING)) head = (struct commit *)o; } - /* We can't generate a cover letter without any patches */ + /* There is nothing to show; it is not an error, though. */ if (!head) return 0; + if (!branch_name) + branch_name = find_branch_name(&rev); } if (ignore_if_in_upstream) { @@ -1294,7 +1358,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, use_stdout, numbered, numbered_files, - origin, nr, list, head, quiet); + origin, nr, list, head, branch_name, quiet); total++; start_number--; } @@ -1366,6 +1430,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) fclose(stdout); } free(list); + free(branch_name); string_list_clear(&extra_to, 0); string_list_clear(&extra_cc, 0); string_list_clear(&extra_hdr, 0); From b7200e839737491dfe8f0297fba54621fd7d7583 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 20 Sep 2011 15:10:08 -0700 Subject: [PATCH 3/6] branch: teach --edit-description option Using branch.$name.description as the configuration key, give users a place to write about what the purpose of the branch is and things like that, so that various subsystems, e.g. "push -s", "request-pull", and "format-patch --cover-letter", can later be taught to use this information. The "-m" option similar to "commit/tag" is deliberately omitted, as the whole point of branch description is about giving descriptive information (the name of the branch itself is a better place for information that fits on a single-line). Signed-off-by: Junio C Hamano --- Documentation/git-branch.txt | 5 ++++ builtin/branch.c | 56 ++++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 507b8d0ab2..8871a4ec21 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -14,6 +14,7 @@ SYNOPSIS 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] [] 'git branch' (-m | -M) [] 'git branch' (-d | -D) [-r] ... +'git branch' --edit-description [] DESCRIPTION ----------- @@ -144,6 +145,10 @@ start-point is either a local or remote-tracking branch. like '--track' would when creating the branch, except that where branch points to is not changed. +--edit-description:: + Open an editor and edit the text to explain what the branch is + for, to be used by various other commands (e.g. `request-pull`). + --contains :: Only list branches which contain the specified commit. diff --git a/builtin/branch.c b/builtin/branch.c index f49596f826..fffa319e0e 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -606,11 +606,49 @@ static int opt_parse_merge_filter(const struct option *opt, const char *arg, int return 0; } +static const char edit_description[] = "BRANCH_DESCRIPTION"; + +static int edit_branch_description(const char *branch_name) +{ + FILE *fp; + int status; + struct strbuf buf = STRBUF_INIT; + struct strbuf name = STRBUF_INIT; + + read_branch_desc(&buf, branch_name); + if (!buf.len || buf.buf[buf.len-1] != '\n') + strbuf_addch(&buf, '\n'); + strbuf_addf(&buf, + "# Please edit the description for the branch\n" + "# %s\n" + "# Lines starting with '#' will be stripped.\n", + branch_name); + fp = fopen(git_path(edit_description), "w"); + if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) { + strbuf_release(&buf); + return error(_("could not write branch description template: %s\n"), + strerror(errno)); + } + strbuf_reset(&buf); + if (launch_editor(git_path(edit_description), &buf, NULL)) { + strbuf_release(&buf); + return -1; + } + stripspace(&buf, 1); + + strbuf_addf(&name, "branch.%s.description", branch_name); + status = git_config_set(name.buf, buf.buf); + strbuf_release(&name); + strbuf_release(&buf); + + return status; +} + int cmd_branch(int argc, const char **argv, const char *prefix) { int delete = 0, rename = 0, force_create = 0; int verbose = 0, abbrev = -1, detached = 0; - int reflog = 0; + int reflog = 0, edit_description = 0; enum branch_track track; int kinds = REF_LOCAL_BRANCH; struct commit_list *with_commit = NULL; @@ -648,6 +686,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1), OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2), OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"), + OPT_BOOLEAN(0, "edit-description", &edit_description, + "edit the description for the branch"), OPT__FORCE(&force_create, "force creation (when already exists)"), { OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref, @@ -694,7 +734,19 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (delete) return delete_branches(argc, argv, delete > 1, kinds); - else if (argc == 0) + else if (edit_description) { + const char *branch_name; + if (detached) + die("Cannot give description to detached HEAD"); + if (!argc) + branch_name = head; + else if (argc == 1) + branch_name = argv[0]; + else + usage_with_options(builtin_branch_usage, options); + if (edit_branch_description(branch_name)) + return 1; + } else if (argc == 0) return print_ref_list(kinds, detached, verbose, abbrev, with_commit); else if (rename && (argc == 1)) rename_branch(head, argv[0], rename > 1); From 3c9f1e7c11186f4c7b39a0e966428587ab20fda5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 16 Sep 2011 11:22:57 -0700 Subject: [PATCH 4/6] request-pull: modernize style Make it a bit more conforming to Documentation/Codingstyle Signed-off-by: Junio C Hamano --- git-request-pull.sh | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/git-request-pull.sh b/git-request-pull.sh index fc080cc5e4..afb75e80b6 100755 --- a/git-request-pull.sh +++ b/git-request-pull.sh @@ -35,27 +35,24 @@ do shift done -base=$1 -url=$2 -head=${3-HEAD} +base=$1 url=$2 head=${3-HEAD} -[ "$base" ] || usage -[ "$url" ] || usage +test -n "$base" && test -n "$url" || usage +baserev=$(git rev-parse --verify "$base"^0) && +headrev=$(git rev-parse --verify "$head"^0) || exit -baserev=`git rev-parse --verify "$base"^0` && -headrev=`git rev-parse --verify "$head"^0` || exit - -merge_base=`git merge-base $baserev $headrev` || +merge_base=$(git merge-base $baserev $headrev) || die "fatal: No commits in common between $base and $head" -branch=$(git ls-remote "$url" \ - | sed -n -e "/^$headrev refs.heads./{ - s/^.* refs.heads.// - p - q - }") +find_matching_branch="/^$headrev "'refs\/heads\//{ + s/^.* refs\/heads\/// + p + q +}' +branch=$(git ls-remote "$url" | sed -n -e "$find_matching_branch") url=$(git ls-remote --get-url "$url") -if [ -z "$branch" ]; then +if test -z "$branch" +then echo "warn: No branch of $url is at:" >&2 git log --max-count=1 --pretty='tformat:warn: %h: %s' $headrev >&2 echo "warn: Are you sure you pushed $head there?" >&2 From cf7316663e2ae459591bc52f0a1c91c3a684e48b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 16 Sep 2011 11:37:08 -0700 Subject: [PATCH 5/6] request-pull: state what commit to expect The message gives a detailed explanation of the commit the requester based the changes on, but lacks information that is necessary for the person who performs a fetch & merge in order to verify that the correct branch was fetched when responding to the pull request. Add a few more lines to describe the commit at the tip expected to be fetched to the same level of detail as the base commit. Also update the warning message slightly when the script notices that the commit may not have been pushed. Signed-off-by: Junio C Hamano --- git-request-pull.sh | 34 +++++++++++++++++++--------------- t/t5150-request-pull.sh | 6 ++++++ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/git-request-pull.sh b/git-request-pull.sh index afb75e80b6..438e7eb957 100755 --- a/git-request-pull.sh +++ b/git-request-pull.sh @@ -35,7 +35,7 @@ do shift done -base=$1 url=$2 head=${3-HEAD} +base=$1 url=$2 head=${3-HEAD} status=0 test -n "$base" && test -n "$url" || usage baserev=$(git rev-parse --verify "$base"^0) && @@ -51,25 +51,29 @@ find_matching_branch="/^$headrev "'refs\/heads\//{ }' branch=$(git ls-remote "$url" | sed -n -e "$find_matching_branch") url=$(git ls-remote --get-url "$url") -if test -z "$branch" -then - echo "warn: No branch of $url is at:" >&2 - git log --max-count=1 --pretty='tformat:warn: %h: %s' $headrev >&2 - echo "warn: Are you sure you pushed $head there?" >&2 - echo >&2 - echo >&2 - branch=..BRANCH.NOT.VERIFIED.. - status=1 -fi git show -s --format='The following changes since commit %H: %s (%ci) -are available in the git repository at:' $baserev && -echo " $url $branch" && -echo && +are available in the git repository at: +' $baserev && +echo " $url${branch+ $branch}" && +git show -s --format=' +for you to fetch changes up to %H: + + %s (%ci) + +----------------------------------------------------------------' $headrev && git shortlog ^$baserev $headrev && -git diff -M --stat --summary $patch $merge_base..$headrev || exit +git diff -M --stat --summary $patch $merge_base..$headrev || status=1 + +if test -z "$branch" +then + echo "warn: No branch of $url is at:" >&2 + git show -s --format='warn: %h: %s' $headrev >&2 + echo "warn: Are you sure you pushed '$head' there?" >&2 + status=1 +fi exit $status diff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh index 9cc0a42ea9..5bd1682944 100755 --- a/t/t5150-request-pull.sh +++ b/t/t5150-request-pull.sh @@ -193,8 +193,14 @@ test_expect_success 'pull request format' ' SUBJECT (DATE) are available in the git repository at: + URL BRANCH + for you to fetch changes up to OBJECT_NAME: + + SUBJECT (DATE) + + ---------------------------------------------------------------- SHORTLOG DIFFSTAT From c0168147831fce00975949213eef3471b7a2b76b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 20 Sep 2011 15:52:57 -0700 Subject: [PATCH 6/6] request-pull: use the branch description Now we have branch descriptions stored in the repository, we can use it when preparing the request-pull message. Signed-off-by: Junio C Hamano --- git-request-pull.sh | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/git-request-pull.sh b/git-request-pull.sh index 438e7eb957..626cf2591a 100755 --- a/git-request-pull.sh +++ b/git-request-pull.sh @@ -35,7 +35,18 @@ do shift done -base=$1 url=$2 head=${3-HEAD} status=0 +base=$1 url=$2 head=${3-HEAD} status=0 branch_name= + +headref=$(git symbolic-ref -q "$head") +if git show-ref -q --verify "$headref" +then + branch_name=${headref#refs/heads/} + if test "z$branch_name" = "z$headref" || + ! git config "branch.$branch_name.description" >/dev/null + then + branch_name= + fi +fi test -n "$base" && test -n "$url" || usage baserev=$(git rev-parse --verify "$base"^0) && @@ -66,6 +77,13 @@ for you to fetch changes up to %H: ----------------------------------------------------------------' $headrev && +if test -n "$branch_name" +then + echo "(from the branch description for $branch local branch)" + echo + git config "branch.$branch_name.description" + echo "----------------------------------------------------------------" +fi && git shortlog ^$baserev $headrev && git diff -M --stat --summary $patch $merge_base..$headrev || status=1