* [PATCH 2nd try] Make git-fmt-merge-msg a builtin
@ 2006-07-03 15:18 Johannes Schindelin
2006-07-03 22:39 ` Junio C Hamano
0 siblings, 1 reply; 5+ messages in thread
From: Johannes Schindelin @ 2006-07-03 15:18 UTC (permalink / raw)
To: git, junio, Timo Hirvonen
Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
This retires git-fmt-merge-msg.perl, since it passes all the
tests, but removes the Perl version not now. Did I mention it
is very, very, very fast? (Even compared to the Git.pm version;
measured on a cygwin setup.)
Makefile | 7 +
builtin-fmt-merge-msg.c | 355 +++++++++++++++++++++++++++++++++++++++++++++++
builtin.h | 1
git.c | 3
4 files changed, 362 insertions(+), 4 deletions(-)
diff --git a/Makefile b/Makefile
index c142bdd..a551c33 100644
--- a/Makefile
+++ b/Makefile
@@ -147,7 +147,7 @@ SCRIPT_SH = \
SCRIPT_PERL = \
git-archimport.perl git-cvsimport.perl git-relink.perl \
- git-shortlog.perl git-fmt-merge-msg.perl git-rerere.perl \
+ git-shortlog.perl git-rerere.perl \
git-annotate.perl git-cvsserver.perl \
git-svnimport.perl git-mv.perl git-cvsexportcommit.perl \
git-send-email.perl
@@ -189,7 +189,8 @@ BUILT_INS = git-log$X git-whatchanged$X
git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \
git-read-tree$X git-commit-tree$X git-write-tree$X \
git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \
- git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X
+ git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X \
+ git-fmt-merge-msg$X
# what 'all' will build and 'install' will install, in gitexecdir
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@@ -245,7 +246,7 @@ BUILTIN_OBJS = \
builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \
- builtin-update-ref.o
+ builtin-update-ref.o builtin-fmt-merge-msg.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
EXTLIBS = -lz
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
new file mode 100644
index 0000000..6527482
--- /dev/null
+++ b/builtin-fmt-merge-msg.c
@@ -0,0 +1,355 @@
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "tag.h"
+
+static const char *fmt_merge_msg_usage =
+ "git-fmt-merge-msg [--summary] [--no-summary] [--file <file>]";
+
+static int merge_summary = 0;
+
+static int fmt_merge_msg_config(const char *key, const char *value)
+{
+ if (!strcmp("merge.summary", key))
+ merge_summary = git_config_bool(key, value);
+ return 0;
+}
+
+struct list {
+ char **list;
+ void **payload;
+ unsigned nr, alloc;
+};
+
+static void append_to_list(struct list *list, char *value, void *payload)
+{
+ if (list->nr == list->alloc) {
+ list->alloc += 32;
+ list->list = realloc(list->list, sizeof(char *) * list->alloc);
+ list->payload = realloc(list->payload,
+ sizeof(char *) * list->alloc);
+ }
+ list->payload[list->nr] = payload;
+ list->list[list->nr++] = value;
+}
+
+static int find_in_list(struct list *list, char *value)
+{
+ int i;
+
+ for (i = 0; i < list->nr; i++)
+ if (!strcmp(list->list[i], value))
+ return i;
+
+ return -1;
+}
+
+static void free_list(struct list *list)
+{
+ int i;
+
+ if (list->alloc == 0)
+ return;
+
+ for (i = 0; i < list->nr; i++) {
+ free(list->list[i]);
+ if (list->payload[i])
+ free(list->payload[i]);
+ }
+ free(list->list);
+ free(list->payload);
+ list->nr = list->alloc = 0;
+}
+
+struct src_data {
+ struct list branch, tag, r_branch, generic;
+ int head_status;
+};
+
+static struct list srcs = { NULL, NULL, 0, 0};
+static struct list origins = { NULL, NULL, 0, 0};
+
+static int handle_line(char *line)
+{
+ int i, len = strlen(line);
+ unsigned char *sha1;
+ char *src, *origin;
+ struct src_data *src_data;
+
+ if (len < 43 || line[40] != '\t')
+ return 1;
+
+ if (!strncmp(line + 41, "not-for-merge", 13))
+ return 0;
+
+ if (line[41] != '\t')
+ return 2;
+
+ line[40] = 0;
+ sha1 = xmalloc(20);
+ i = get_sha1(line, sha1);
+ line[40] = '\t';
+ if (i)
+ return 3;
+
+ if (line[len - 1] == '\n')
+ line[len - 1] = 0;
+ line += 42;
+
+ src = strstr(line, " of ");
+ if (src) {
+ *src = 0;
+ src += 4;
+ } else
+ src = "HEAD";
+
+ i = find_in_list(&srcs, src);
+ if (i < 0) {
+ i = srcs.nr;
+ append_to_list(&srcs, strdup(src),
+ xcalloc(1, sizeof(struct src_data)));
+ }
+ src_data = srcs.payload[i];
+
+ if (!strncmp(line, "branch ", 7)) {
+ origin = strdup(line + 7);
+ append_to_list(&src_data->branch, origin, NULL);
+ src_data->head_status |= 2;
+ } else if (!strncmp(line, "tag ", 4)) {
+ origin = line;
+ append_to_list(&src_data->tag, strdup(origin + 4), NULL);
+ src_data->head_status |= 2;
+ } else if (!strncmp(line, "remote branch ", 14)) {
+ origin = strdup(line + 14);
+ append_to_list(&src_data->r_branch, origin, NULL);
+ src_data->head_status |= 2;
+ } else if (!strcmp(line, "HEAD")) {
+ origin = strdup(src);
+ src_data->head_status |= 1;
+ } else {
+ origin = strdup(src);
+ append_to_list(&src_data->generic, strdup(line), NULL);
+ src_data->head_status |= 2;
+ }
+
+ if (!strcmp(".", src) || !strcmp(src, origin)) {
+ int len = strlen(origin);
+ if (origin[0] == '\'' && origin[len - 1] == '\'') {
+ char *new_origin = malloc(len - 1);
+ memcpy(new_origin, origin + 1, len - 2);
+ new_origin[len - 1] = 0;
+ origin = new_origin;
+ } else
+ origin = strdup(origin);
+ } else {
+ char *new_origin = malloc(strlen(origin) + strlen(src) + 5);
+ sprintf(new_origin, "%s of %s", origin, src);
+ origin = new_origin;
+ }
+ append_to_list(&origins, origin, sha1);
+ return 0;
+}
+
+static void print_joined(const char *singular, const char *plural,
+ struct list *list)
+{
+ if (list->nr == 0)
+ return;
+ if (list->nr == 1) {
+ printf("%s%s", singular, list->list[0]);
+ } else {
+ int i;
+ printf("%s", plural);
+ for (i = 0; i < list->nr - 1; i++)
+ printf("%s%s", i > 0 ? ", " : "", list->list[i]);
+ printf(" and %s", list->list[list->nr - 1]);
+ }
+}
+
+static void shortlog(const char *name, unsigned char *sha1,
+ struct commit *head, struct rev_info *rev, int limit)
+{
+ int i, count = 0;
+ struct commit *commit;
+ struct object *branch;
+ struct list subjects = { NULL, NULL, 0, 0 };
+ int flags = UNINTERESTING | TREECHANGE | SEEN | SHOWN | ADDED;
+
+ branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40);
+ if (!branch || branch->type != TYPE_COMMIT)
+ return;
+
+ setup_revisions(0, NULL, rev, NULL);
+ rev->ignore_merges = 1;
+ add_pending_object(rev, branch, name);
+ add_pending_object(rev, &head->object, "^HEAD");
+ head->object.flags |= UNINTERESTING;
+ prepare_revision_walk(rev);
+ while ((commit = get_revision(rev)) != NULL) {
+ char *oneline, *bol, *eol;
+
+ /* ignore merges */
+ if (commit->parents && commit->parents->next)
+ continue;
+
+ count++;
+ if (subjects.nr > limit)
+ continue;
+
+ bol = strstr(commit->buffer, "\n\n");
+ if (!bol) {
+ append_to_list(&subjects, strdup(sha1_to_hex(
+ commit->object.sha1)),
+ NULL);
+ continue;
+ }
+
+ bol += 2;
+ eol = strchr(bol, '\n');
+
+ if (eol) {
+ int len = eol - bol;
+ oneline = malloc(len + 1);
+ memcpy(oneline, bol, len);
+ oneline[len] = 0;
+ } else
+ oneline = strdup(bol);
+ append_to_list(&subjects, oneline, NULL);
+ }
+
+ if (count > limit)
+ printf("\n* %s: (%d commits)\n", name, count);
+ else
+ printf("\n* %s:\n", name);
+
+ for (i = 0; i < subjects.nr; i++)
+ if (i >= limit)
+ printf(" ...\n");
+ else
+ printf(" %s\n", subjects.list[i]);
+
+ clear_commit_marks((struct commit *)branch, flags);
+ clear_commit_marks(head, flags);
+ free_commit_list(rev->commits);
+ rev->commits = NULL;
+ rev->pending.nr = 0;
+
+ free_list(&subjects);
+}
+
+int cmd_fmt_merge_msg(int argc, char **argv, char **envp)
+{
+ int limit = 20, i = 0;
+ char line[1024];
+ FILE *in = stdin;
+ const char *sep = "";
+ unsigned char head_sha1[20];
+ const char *head, *current_branch;
+
+ git_config(fmt_merge_msg_config);
+
+ while (argc > 1) {
+ if (!strcmp(argv[1], "--summary"))
+ merge_summary = 1;
+ else if (!strcmp(argv[1], "--no-summary"))
+ merge_summary = 0;
+ else if (!strcmp(argv[1], "-F") || !strcmp(argv[1], "--file")) {
+ if (argc < 2)
+ die ("Which file?");
+ if (!strcmp(argv[2], "-"))
+ in = stdin;
+ else {
+ fclose(in);
+ in = fopen(argv[2], "r");
+ }
+ argc--; argv++;
+ } else
+ break;
+ argc--; argv++;
+ }
+
+ if (argc > 1)
+ usage(fmt_merge_msg_usage);
+
+ /* get current branch */
+ head = strdup(git_path("HEAD"));
+ current_branch = resolve_ref(head, head_sha1, 1);
+ current_branch += strlen(head) - 4;
+ free((char *)head);
+ if (!strncmp(current_branch, "refs/heads/", 11))
+ current_branch += 11;
+
+ while (fgets(line, sizeof(line), in)) {
+ i++;
+ if (line[0] == 0)
+ continue;
+ if (handle_line(line))
+ die ("Error in line %d: %s", i, line);
+ }
+
+ printf("Merge ");
+ for (i = 0; i < srcs.nr; i++) {
+ struct src_data *src_data = srcs.payload[i];
+ const char *subsep = "";
+
+ printf(sep);
+ sep = "; ";
+
+ if (src_data->head_status == 1) {
+ printf(srcs.list[i]);
+ continue;
+ }
+ if (src_data->head_status == 3) {
+ subsep = ", ";
+ printf("HEAD");
+ }
+ if (src_data->branch.nr) {
+ printf(subsep);
+ subsep = ", ";
+ print_joined("branch ", "branches ", &src_data->branch);
+ }
+ if (src_data->r_branch.nr) {
+ printf(subsep);
+ subsep = ", ";
+ print_joined("remote branch ", "remote branches ",
+ &src_data->r_branch);
+ }
+ if (src_data->tag.nr) {
+ printf(subsep);
+ subsep = ", ";
+ print_joined("tag ", "tags ", &src_data->tag);
+ }
+ if (src_data->generic.nr) {
+ printf(subsep);
+ print_joined("commit ", "commits ", &src_data->generic);
+ }
+ if (strcmp(".", srcs.list[i]))
+ printf(" of %s", srcs.list[i]);
+ }
+
+ if (!strcmp("master", current_branch))
+ putchar('\n');
+ else
+ printf(" into %s\n", current_branch);
+
+ if (merge_summary) {
+ struct commit *head;
+ struct rev_info rev;
+
+ head = lookup_commit(head_sha1);
+ init_revisions(&rev);
+ rev.commit_format = CMIT_FMT_ONELINE;
+ rev.ignore_merges = 1;
+ rev.limited = 1;
+
+ for (i = 0; i < origins.nr; i++)
+ shortlog(origins.list[i], origins.payload[i],
+ head, &rev, limit);
+ }
+
+ /* No cleanup yet; is standalone anyway */
+
+ return 0;
+}
+
diff --git a/builtin.h b/builtin.h
old mode 100644
new mode 100755
index f12d5e6..d9e5483
--- a/builtin.h
+++ b/builtin.h
@@ -49,6 +49,7 @@ extern int cmd_cat_file(int argc, const
extern int cmd_rev_parse(int argc, const char **argv, char **envp);
extern int cmd_update_index(int argc, const char **argv, char **envp);
extern int cmd_update_ref(int argc, const char **argv, char **envp);
+extern int cmd_fmt_merge_msg(int argc, const char **argv, char **envp);
extern int cmd_write_tree(int argc, const char **argv, char **envp);
extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
diff --git a/git.c b/git.c
index 512fa63..7cc826b 100644
--- a/git.c
+++ b/git.c
@@ -200,7 +200,8 @@ static void handle_internal_command(int
{ "mailinfo", cmd_mailinfo },
{ "stripspace", cmd_stripspace },
{ "update-index", cmd_update_index },
- { "update-ref", cmd_update_ref }
+ { "update-ref", cmd_update_ref },
+ { "fmt-merge-msg", cmd_fmt_merge_msg }
};
int i;
--
1.4.1.gb2dcd-dirty
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH 2nd try] Make git-fmt-merge-msg a builtin
2006-07-03 15:18 [PATCH 2nd try] Make git-fmt-merge-msg a builtin Johannes Schindelin
@ 2006-07-03 22:39 ` Junio C Hamano
2006-07-04 7:50 ` Johannes Schindelin
0 siblings, 1 reply; 5+ messages in thread
From: Junio C Hamano @ 2006-07-03 22:39 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
>
> ---
> This retires git-fmt-merge-msg.perl, since it passes all the
> tests, but removes the Perl version not now.
There is no point not removing the script if you update git.c
and Makefile to point at the new one.
We do not have a test specific for fmt-merge-msg, so it
obviously passes all the tests ;-). A new one is attached.
I think we should extend boolean to accept 'yes' and 'no', as I
suggested earlier on the list, but other than that things look
good.
Thanks for the patch; no need to resubmit -- I'll munge the
points I raised above.
-- >8 --
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
new file mode 100755
index 0000000..63e49f3
--- /dev/null
+++ b/t/t6200-fmt-merge-msg.sh
@@ -0,0 +1,163 @@
+#!/bin/sh
+#
+# Copyright (c) 2006, Junio C Hamano
+#
+
+test_description='fmt-merge-msg test'
+
+. ./test-lib.sh
+
+datestamp=1151939923
+setdate () {
+ GIT_COMMITTER_DATE="$datestamp +0200"
+ GIT_AUTHOR_DATE="$datestamp +0200"
+ datestamp=`expr "$datestamp" + 1`
+ export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+}
+
+test_expect_success setup '
+ echo one >one &&
+ git add one &&
+ setdate &&
+ git commit -m "Initial" &&
+
+ echo uno >one &&
+ echo dos >two &&
+ git add two &&
+ setdate &&
+ git commit -a -m "Second" &&
+
+ git checkout -b left &&
+
+ echo $datestamp >one &&
+ setdate &&
+ git commit -a -m "Common #1" &&
+
+ echo $datestamp >one &&
+ setdate &&
+ git commit -a -m "Common #2" &&
+
+ git branch right &&
+
+ echo $datestamp >two &&
+ setdate &&
+ git commit -a -m "Left #3" &&
+
+ echo $datestamp >two &&
+ setdate &&
+ git commit -a -m "Left #4" &&
+
+ echo $datestamp >two &&
+ setdate &&
+ git commit -a -m "Left #5" &&
+
+ git checkout right &&
+
+ echo $datestamp >three &&
+ git add three &&
+ setdate &&
+ git commit -a -m "Right #3" &&
+
+ echo $datestamp >three &&
+ setdate &&
+ git commit -a -m "Right #4" &&
+
+ echo $datestamp >three &&
+ setdate &&
+ git commit -a -m "Right #5" &&
+
+ git show-branch
+'
+
+cat >expected <<\EOF
+Merge branch 'left'
+EOF
+
+test_expect_success 'merge-msg test #1' '
+
+ git checkout master &&
+ git fetch . left &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ diff -u actual expected
+'
+
+cat >expected <<\EOF
+Merge branch 'left' of ../trash
+EOF
+
+test_expect_success 'merge-msg test #2' '
+
+ git checkout master &&
+ git fetch ../trash left &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ diff -u actual expected
+'
+
+cat >expected <<\EOF
+Merge branch 'left'
+
+* left:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+EOF
+
+test_expect_success 'merge-msg test #3' '
+
+ git repo-config merge.summary true &&
+
+ git checkout master &&
+ setdate &&
+ git fetch . left &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ diff -u actual expected
+'
+
+cat >expected <<\EOF
+Merge branches 'left' and 'right'
+
+* left:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+
+* right:
+ Right #5
+ Right #4
+ Right #3
+ Common #2
+ Common #1
+EOF
+
+test_expect_success 'merge-msg test #4' '
+
+ git repo-config merge.summary true &&
+
+ git checkout master &&
+ setdate &&
+ git fetch . left right &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ diff -u actual expected
+'
+
+test_expect_success 'merge-msg test #5' '
+
+ git repo-config merge.summary yes &&
+
+ git checkout master &&
+ setdate &&
+ git fetch . left right &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ diff -u actual expected
+'
+
+test_done
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH 2nd try] Make git-fmt-merge-msg a builtin
2006-07-03 22:39 ` Junio C Hamano
@ 2006-07-04 7:50 ` Johannes Schindelin
2006-07-04 7:56 ` Junio C Hamano
0 siblings, 1 reply; 5+ messages in thread
From: Johannes Schindelin @ 2006-07-04 7:50 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Hi,
On Mon, 3 Jul 2006, Junio C Hamano wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
> > Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
> >
> > ---
> > This retires git-fmt-merge-msg.perl, since it passes all the
> > tests, but removes the Perl version not now.
>
> There is no point not removing the script if you update git.c
> and Makefile to point at the new one.
I should have put PATH/RFC. I did not want to remove the script right
away, so people could compare outputs (like Martin did with
git-format-patch).
> We do not have a test specific for fmt-merge-msg, so it
> obviously passes all the tests ;-). A new one is attached.
Well, t4013 showed me two output bugs, but it evidently did not check for
merge.summary.
> I think we should extend boolean to accept 'yes' and 'no',
In that case, could we have "YesPlease", "oui", "si" and "da", too? ;-)
Ciao,
Dscho
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2nd try] Make git-fmt-merge-msg a builtin
2006-07-04 7:50 ` Johannes Schindelin
@ 2006-07-04 7:56 ` Junio C Hamano
2006-07-04 8:18 ` Johannes Schindelin
0 siblings, 1 reply; 5+ messages in thread
From: Junio C Hamano @ 2006-07-04 7:56 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> I should have put PATH/RFC. I did not want to remove the script right
> away, so people could compare outputs (like Martin did with
> git-format-patch).
Well the stuff is quite straightforward and I think we could fix
it in-tree while in "next".
>> I think we should extend boolean to accept 'yes' and 'no',
>
> In that case, could we have "YesPlease", "oui", "si" and "da", too? ;-)
Hai? Nah, that's too much.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2nd try] Make git-fmt-merge-msg a builtin
2006-07-04 7:56 ` Junio C Hamano
@ 2006-07-04 8:18 ` Johannes Schindelin
0 siblings, 0 replies; 5+ messages in thread
From: Johannes Schindelin @ 2006-07-04 8:18 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Hai^H^H^HHi,
On Tue, 4 Jul 2006, Junio C Hamano wrote:
> >> I think we should extend boolean to accept 'yes' and 'no',
> >
> > In that case, could we have "YesPlease", "oui", "si" and "da", too? ;-)
>
> Hai? Nah, that's too much.
You know, I always wondered if we could introduce "perhaps", for us
procrastinators.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2006-07-04 8:19 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-07-03 15:18 [PATCH 2nd try] Make git-fmt-merge-msg a builtin Johannes Schindelin
2006-07-03 22:39 ` Junio C Hamano
2006-07-04 7:50 ` Johannes Schindelin
2006-07-04 7:56 ` Junio C Hamano
2006-07-04 8:18 ` Johannes Schindelin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox