{"thread":{"id":"20011","subject":"[PATCH] add --summary option to git-push and git-fetch","startedAt":"2009-07-03T04:48:01Z","lastAt":"2010-02-04T17:55:13Z","messageCount":23,"participants":["Larry D'Anna","Junio C Hamano","Stephen Boyd","Ilari Liusvaara","Tay Ray Chuan","Daniel Barkalow"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"117400","messageId":"20090703044801.GA2072@cthulhu","threadId":"20011","inReplyTo":null,"subject":"[PATCH] add --summary option to git-push and git-fetch","fromName":"Larry D'Anna","fromEmail":"larry@elder-gods.org","sentAt":"2009-07-03T04:48:01Z","receivedAt":"2009-07-03T04:48:01Z","isPatch":true,"sender":{"key":"larry@elder-gods.org","avatar":"https://avatars.githubusercontent.com/u/3013304?v=4"},"body":"--summary will cause git-push to output a one-line of each commit pushed.\n--summary=n will display at most n commits for each ref pushed.\n\n$ git push --dry-run --summary origin :\nTo /home/larry/gitsandbox/a\n   80f0e50..5593a38  master -> master\n    > 5593a38 foo\n    > 81c03f8 bar\n\nFetch works the same way.\n\nSigned-off-by: Larry D'Anna <larry@elder-gods.org>\n---\n        \nThis patch should be applied on top of 1965ff7 add --porcelain option to git-push\n\nDifferences since the last version of this patch:\n\n* added --summary to fetch \n\n* commits are marked by < or > like --left-right\n\n* fixed a bug where it died gracelessly on a forced push A...B if A is not in \n  the local repository.\n\n* fixed a bug where cmd_log was modifying the part lists of commits.\n\n* called get_revision and friends directly instead of using cmd_log\n\n\n Documentation/fetch-options.txt |    7 +++++++\n Documentation/git-push.txt      |    6 ++++++\n builtin-fetch.c                 |   24 ++++++++++++++++++------\n builtin-log.c                   |   35 +++++++++++++++++++++++++++++++++++\n builtin-push.c                  |   12 +++++++++---\n builtin.h                       |    2 ++\n transport.c                     |   39 +++++++++++++++++++++++++++------------\n transport.h                     |    2 +-\n 8 files changed, 105 insertions(+), 22 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex d313795..2e66d5e 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -27,6 +27,13 @@\n \tfetches is a descendant of `<lbranch>`.  This option\n \toverrides that check.\n \n+--summary::\n+\tPrint a one-line summary of each commit fetched.\n+\n+--summary=<n>::\n+\tLike --summary, but with a limit of <n> commits per ref.\n+\n+\n ifdef::git-pull[]\n --no-tags::\n endif::git-pull[]\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 2653388..803fe36 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -85,6 +85,12 @@ nor in any Push line of the corresponding remotes file---see below).\n --dry-run::\n \tDo everything except actually send the updates.\n \n+--summary::\n+\tPrint a one-line summary of each commit pushed.\n+\n+--summary=<n>::\n+\tLike --summary, but with a limit of <n> commits per ref.\n+\n --porcelain::\n \tProduce machine-readable output.  The output status line for each ref\n \twill be tab-separated and sent to stdout instead of stderr.  The full\ndiff --git a/builtin-fetch.c b/builtin-fetch.c\nindex cd5eb9a..b79d870 100644\n--- a/builtin-fetch.c\n+++ b/builtin-fetch.c\n@@ -29,6 +29,7 @@ static const char *depth;\n static const char *upload_pack;\n static struct strbuf default_rla = STRBUF_INIT;\n static struct transport *transport;\n+static int summary = 0;\n \n static struct option builtin_fetch_options[] = {\n \tOPT__VERBOSITY(&verbosity),\n@@ -47,6 +48,9 @@ static struct option builtin_fetch_options[] = {\n \t\t    \"allow updating of HEAD ref\"),\n \tOPT_STRING(0, \"depth\", &depth, \"DEPTH\",\n \t\t   \"deepen history of shallow clone\"),\n+\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] fetched commits\",\n+\t  PARSE_OPT_OPTARG, NULL, -1\n+\t},\n \tOPT_END()\n };\n \n@@ -197,7 +201,8 @@ static int s_update_ref(const char *action,\n \n static int update_local_ref(struct ref *ref,\n \t\t\t    const char *remote,\n-\t\t\t    char *display)\n+\t\t\t    char *display,\n+\t\t\t    char *quickref)\n {\n \tstruct commit *current = NULL, *updated;\n \tenum object_type type;\n@@ -260,11 +265,12 @@ static int update_local_ref(struct ref *ref,\n \t\tsprintf(display, \"%c %-*s %-*s -> %s%s\", r ? '!' : '*',\n \t\t\tSUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,\n \t\t\tr ? \"  (unable to update local ref)\" : \"\");\n+\t\tif (!r)\n+\t\t\tstrcpy (quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));\n \t\treturn r;\n \t}\n \n \tif (in_merge_bases(current, &updated, 1)) {\n-\t\tchar quickref[83];\n \t\tint r;\n \t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n \t\tstrcat(quickref, \"..\");\n@@ -275,7 +281,6 @@ static int update_local_ref(struct ref *ref,\n \t\t\tpretty_ref, r ? \"  (unable to update local ref)\" : \"\");\n \t\treturn r;\n \t} else if (force || ref->force) {\n-\t\tchar quickref[84];\n \t\tint r;\n \t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n \t\tstrcat(quickref, \"...\");\n@@ -301,6 +306,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \tstruct commit *commit;\n \tint url_len, i, note_len, shown_url = 0, rc = 0;\n \tchar note[1024];\n+\tchar quickref[84];\n \tconst char *what, *kind;\n \tstruct ref *rm;\n \tchar *url, *filename = git_path(\"FETCH_HEAD\");\n@@ -373,12 +379,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\t\tfputc(url[i], fp);\n \t\tfputc('\\n', fp);\n \n-\t\tif (ref)\n-\t\t\trc |= update_local_ref(ref, what, note);\n-\t\telse\n+\t\tif (ref) {\n+\t\t\t*quickref = 0;\n+\t\t\trc |= update_local_ref(ref, what, note, quickref);\n+\t\t} else {\n+\t\t\tstrcpy (quickref, find_unique_abbrev(rm->old_sha1, DEFAULT_ABBREV));\n \t\t\tsprintf(note, \"* %-*s %-*s -> FETCH_HEAD\",\n \t\t\t\tSUMMARY_WIDTH, *kind ? kind : \"branch\",\n \t\t\t\t REFCOL_WIDTH, *what ? what : \"HEAD\");\n+\t\t}\n \t\tif (*note) {\n \t\t\tif (verbosity >= 0 && !shown_url) {\n \t\t\t\tfprintf(stderr, \"From %.*s\\n\",\n@@ -388,6 +397,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\tif (verbosity >= 0)\n \t\t\t\tfprintf(stderr, \" %s\\n\", note);\n \t\t}\n+\t\tif (summary && quickref[0])\n+\t\t\tprint_summary_for_push_or_fetch (quickref, summary);\n+\n \t}\n \tfree(url);\n \tfclose(fp);\ndiff --git a/builtin-log.c b/builtin-log.c\nindex 44f9a27..cc4dc0a 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -1293,3 +1293,38 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \tfree_patch_ids(&ids);\n \treturn 0;\n }\n+\n+\n+void print_summary_for_push_or_fetch (const char *quickref, int limit)\n+{\n+\tstruct rev_info rev;\n+\tFILE *temp;\n+\tint i, max;\n+\tstruct object *obj;\n+\tstruct commit *commit;\n+\n+\ttemp = stdout;\n+\tstdout = stderr;\n+\n+\tmax = get_max_object_index();\n+\tfor (i = 0; i < max; i++)  {\n+\t\tobj = get_indexed_object(i);\n+\t\tif (obj)\n+\t\t\tobj->flags &= ~ALL_REV_FLAGS;\n+\t}\n+\n+\tinit_revisions(&rev, NULL);\n+\tif (limit > 0)\n+\t\trev.max_count = limit;\n+\trev.prune = 0;\n+\trev.verbose_header = 1;\n+\trev.always_show_header = 1;\n+\tget_commit_format(\"    %m %h %s\", &rev);\n+\tassert(!handle_revision_arg(quickref, &rev, 0, 1));\n+\tassert(!prepare_revision_walk(&rev));\n+\n+\twhile ((commit = get_revision(&rev)) != NULL)\n+\t\tlog_tree_commit(&rev, commit);\n+\n+\tstdout = temp;\n+}\ndiff --git a/builtin-push.c b/builtin-push.c\nindex 0a0297f..00cf846 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -113,7 +113,7 @@ static void setup_default_push_refspecs(void)\n \t}\n }\n \n-static int do_push(const char *repo, int flags)\n+static int do_push(const char *repo, int flags, int summary)\n {\n \tint i, errs;\n \tstruct remote *remote = remote_get(repo);\n@@ -173,7 +173,7 @@ static int do_push(const char *repo, int flags)\n \n \t\tif (flags & TRANSPORT_PUSH_VERBOSE)\n \t\t\tfprintf(stderr, \"Pushing to %s\\n\", url[i]);\n-\t\terr = transport_push(transport, refspec_nr, refspec, flags);\n+\t\terr = transport_push(transport, refspec_nr, refspec, flags, summary);\n \t\terr |= transport_disconnect(transport);\n \n \t\tif (!err)\n@@ -192,6 +192,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \tint rc;\n \tconst char *repo = NULL;\t/* default repository */\n \n+\tint summary = 0;\n+\n \tstruct option options[] = {\n \t\tOPT_BIT('v', \"verbose\", &flags, \"be verbose\", TRANSPORT_PUSH_VERBOSE),\n \t\tOPT_STRING( 0 , \"repo\", &repo, \"repository\", \"repository\"),\n@@ -205,6 +207,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOLEAN( 0 , \"thin\", &thin, \"use thin pack\"),\n \t\tOPT_STRING( 0 , \"receive-pack\", &receivepack, \"receive-pack\", \"receive pack program\"),\n \t\tOPT_STRING( 0 , \"exec\", &receivepack, \"receive-pack\", \"receive pack program\"),\n+\t\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] pushed commits\",\n+\t\t  PARSE_OPT_OPTARG, NULL, -1\n+\t\t},\n+\n \t\tOPT_END()\n \t};\n \n@@ -218,7 +224,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tset_refspecs(argv + 1, argc - 1);\n \t}\n \n-\trc = do_push(repo, flags);\n+\trc = do_push(repo, flags, summary);\n \tif (rc == -1)\n \t\tusage_with_options(push_usage, options);\n \telse\ndiff --git a/builtin.h b/builtin.h\nindex 20427d2..1991eb2 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -113,4 +113,6 @@ extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);\n extern int cmd_show_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);\n \n+extern void print_summary_for_push_or_fetch (const char *quickref, int limit);\n+\n #endif\ndiff --git a/transport.c b/transport.c\nindex b074067..139f472 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -11,6 +11,7 @@\n #include \"bundle.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"builtin.h\"\n \n /* rsync support */\n \n@@ -750,17 +751,20 @@ static const char *status_abbrev(unsigned char sha1[20])\n \treturn find_unique_abbrev(sha1, DEFAULT_ABBREV);\n }\n \n-static void print_ok_ref_status(struct ref *ref, int porcelain)\n+static void print_ok_ref_status(struct ref *ref, int porcelain, int summary)\n {\n+\tchar quickref[84];\n+\tint summary_impossible = 0;\n+\n \tif (ref->deletion)\n \t\tprint_ref_status('-', \"[deleted]\", ref, NULL, NULL, porcelain);\n-\telse if (is_null_sha1(ref->old_sha1))\n+\telse if (is_null_sha1(ref->old_sha1)) {\n \t\tprint_ref_status('*',\n \t\t\t(!prefixcmp(ref->name, \"refs/tags/\") ? \"[new tag]\" :\n \t\t\t\"[new branch]\"),\n \t\t\tref, ref->peer_ref, NULL, porcelain);\n-\telse {\n-\t\tchar quickref[84];\n+\t\tstrcpy (quickref, status_abbrev(ref->new_sha1));\n+\t} else {\n \t\tchar type;\n \t\tconst char *msg;\n \n@@ -769,6 +773,8 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \t\t\tstrcat(quickref, \"...\");\n \t\t\ttype = '+';\n \t\t\tmsg = \"forced update\";\n+\t\t\tif (!lookup_commit_reference_gently(ref->old_sha1, 1))\n+\t\t\t\tsummary_impossible = 1;\n \t\t} else {\n \t\t\tstrcat(quickref, \"..\");\n \t\t\ttype = ' ';\n@@ -778,9 +784,17 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \n \t\tprint_ref_status(type, quickref, ref, ref->peer_ref, msg, porcelain);\n \t}\n+\n+\tif (summary) {\n+\t\tif (summary_impossible) {\n+\t\t\tfprintf (stderr, \"    %s is unavailable\\n\", status_abbrev(ref->old_sha1));\n+\t\t} else {\n+\t\t\tprint_summary_for_push_or_fetch(quickref, summary);\n+\t\t}\n+\t}\n }\n \n-static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)\n+static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain, int summary)\n {\n \tif (!count)\n \t\tfprintf(stderr, \"To %s\\n\", dest);\n@@ -812,7 +826,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n \t\t\t\t\t\t \"remote failed to report status\", porcelain);\n \t\tbreak;\n \tcase REF_STATUS_OK:\n-\t\tprint_ok_ref_status(ref, porcelain);\n+\t\tprint_ok_ref_status(ref, porcelain, summary);\n \t\tbreak;\n \t}\n \n@@ -820,7 +834,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n }\n \n static void print_push_status(const char *dest, struct ref *refs,\n-\t\t\t\t\t\t\t  int verbose, int porcelain)\n+\t\t\t\t\t\t\t  int verbose, int porcelain, int summary)\n {\n \tstruct ref *ref;\n \tint n = 0;\n@@ -828,18 +842,18 @@ static void print_push_status(const char *dest, struct ref *refs,\n \tif (verbose) {\n \t\tfor (ref = refs; ref; ref = ref->next)\n \t\t\tif (ref->status == REF_STATUS_UPTODATE)\n-\t\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \t}\n \n \tfor (ref = refs; ref; ref = ref->next)\n \t\tif (ref->status == REF_STATUS_OK)\n-\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \n \tfor (ref = refs; ref; ref = ref->next) {\n \t\tif (ref->status != REF_STATUS_NONE &&\n \t\t    ref->status != REF_STATUS_UPTODATE &&\n \t\t    ref->status != REF_STATUS_OK)\n-\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \t}\n }\n \n@@ -997,7 +1011,8 @@ int transport_set_option(struct transport *transport,\n }\n \n int transport_push(struct transport *transport,\n-\t\t   int refspec_nr, const char **refspec, int flags)\n+\t\t\t\t   int refspec_nr, const char **refspec,\n+\t\t\t\t   int flags, int summary)\n {\n \tverify_remote_names(refspec_nr, refspec);\n \n@@ -1024,7 +1039,7 @@ int transport_push(struct transport *transport,\n \n \t\tret = transport->push_refs(transport, remote_refs, flags);\n \n-\t\tprint_push_status(transport->url, remote_refs, verbose | porcelain, porcelain);\n+\t\tprint_push_status(transport->url, remote_refs, verbose | porcelain, porcelain, summary);\n \n \t\tif (!(flags & TRANSPORT_PUSH_DRY_RUN)) {\n \t\t\tstruct ref *ref;\ndiff --git a/transport.h b/transport.h\nindex 51b5397..360051e 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -68,7 +68,7 @@ int transport_set_option(struct transport *transport, const char *name,\n \t\t\t const char *value);\n \n int transport_push(struct transport *connection,\n-\t\t   int refspec_nr, const char **refspec, int flags);\n+\t\t\t\t   int refspec_nr, const char **refspec, int flags, int summary);\n \n const struct ref *transport_get_remote_refs(struct transport *transport);\n \n-- \n1.6.3.3.403.gdac20\n"},{"id":"117409","messageId":"7viqiat965.fsf@alter.siamese.dyndns.org","threadId":"20011","inReplyTo":"20090703044801.GA2072@cthulhu","subject":"Re: [PATCH] add --summary option to git-push and git-fetch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-07-03T09:20:18Z","receivedAt":"2009-07-03T09:20:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Larry D'Anna <larry@elder-gods.org> writes:\n\n> --summary will cause git-push to output a one-line of each commit pushed.\n> --summary=n will display at most n commits for each ref pushed.\n>\n> $ git push --dry-run --summary origin :\n> To /home/larry/gitsandbox/a\n>    80f0e50..5593a38  master -> master\n>     > 5593a38 foo\n>     > 81c03f8 bar\n>\n> Fetch works the same way.\n>\n> Signed-off-by: Larry D'Anna <larry@elder-gods.org>\n> ---\n\nWith this rewrite not to call cmd_log() directly, it looks much better\nthan the previous round.  It allows us more freedom to do things slightly\ndifferently than the stock cmd_log() lets us, such as giving \"...\" after\n\"n\" commits by default, much like fmt-merge-msg does.\n\n> diff --git a/builtin-fetch.c b/builtin-fetch.c\n> index cd5eb9a..b79d870 100644\n> --- a/builtin-fetch.c\n> +++ b/builtin-fetch.c\n> @@ -29,6 +29,7 @@ static const char *depth;\n>  static const char *upload_pack;\n>  static struct strbuf default_rla = STRBUF_INIT;\n>  static struct transport *transport;\n> +static int summary = 0;\n\nDon't initialize statics with \"= 0;\".  BSS will take care of it.\n\n>  static struct option builtin_fetch_options[] = {\n>  \tOPT__VERBOSITY(&verbosity),\n> @@ -47,6 +48,9 @@ static struct option builtin_fetch_options[] = {\n>  \t\t    \"allow updating of HEAD ref\"),\n>  \tOPT_STRING(0, \"depth\", &depth, \"DEPTH\",\n>  \t\t   \"deepen history of shallow clone\"),\n> +\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] fetched commits\",\n> +\t  PARSE_OPT_OPTARG, NULL, -1\n> +\t},\n>  \tOPT_END()\n>  };\n\nI think I'd prefer some reasonable default instead of making it unlimited,\nmuch like how fmt-merge-msg does.  We might want to make this configurable\n(I think fmt-merge-msg uses a hardcoded 20), and perhaps even use them the\nsame configuration variable (summary.length or something).\n\n> @@ -260,11 +265,12 @@ static int update_local_ref(struct ref *ref,\n>  \t\tsprintf(display, \"%c %-*s %-*s -> %s%s\", r ? '!' : '*',\n>  \t\t\tSUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,\n>  \t\t\tr ? \"  (unable to update local ref)\" : \"\");\n> +\t\tif (!r)\n> +\t\t\tstrcpy (quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));\n\nWe do not leave extra whitespace between function name and open\nparenthesis (you have other instances of this style violation); on the\nother hand, we do keep one whitespace after keywords like if, while, and\nfor (this is just fyi; I do not think I saw any violation of the latter in\nthe patch).\n\n> diff --git a/builtin-log.c b/builtin-log.c\n> index 44f9a27..cc4dc0a 100644\n> --- a/builtin-log.c\n> +++ b/builtin-log.c\n> @@ -1293,3 +1293,38 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n> ...\n> +void print_summary_for_push_or_fetch (const char *quickref, int limit)\n> +{\n> +...\n> +\ttemp = stdout;\n> +\tstdout = stderr;\n\nIs this even a valid C?  stdout and friends are described in POSIX.1 as\n\"Normally, there are three open streams with constant pointers declared in\nthe <stdio.h> header and associated with the standard open files.\"\n\nAt least Solaris 11 Clib headers does not seem to like it.  I do not know\nabout Windows.\n"},{"id":"117531","messageId":"20090707015948.GA525@cthulhu","threadId":"20011","inReplyTo":"7viqiat965.fsf@alter.siamese.dyndns.org","subject":"[PATCH v3] add --summary option to git-push and git-fetch","fromName":"Larry D'Anna","fromEmail":"larry@elder-gods.org","sentAt":"2009-07-07T01:59:48Z","receivedAt":"2009-07-07T01:59:48Z","isPatch":true,"sender":{"key":"larry@elder-gods.org","avatar":"https://avatars.githubusercontent.com/u/3013304?v=4"},"body":"\n\n--summary will cause git-push to output a one-line of each commit pushed.\n--summary=n will display at most n commits for each ref pushed.\n\n$ git push --dry-run --summary origin :\nTo /home/larry/gitsandbox/a\n   80f0e50..5593a38  master -> master\n    > 5593a38 foo\n    > 81c03f8 bar\n\nFetch works the same way.\n\nSigned-off-by: Larry D'Anna <larry@elder-gods.org>\n---\n\nThis patch is meant to be applied on top of 1965ff7 add --porcelain option to git-push\n\nDifferences since the last version:\n\n* it will print ... at the end of the summary if it stops because there's too many lines\n\n* it prints an extra newline after each summary to make the output more readable\n\n* --summary now defaults to --summary=20 \n\n* whitespace is fixed \n\n* no more stdout = stderr shenanigans\n\n* s/static int summary = 0;/static int summary;/\n\n Documentation/fetch-options.txt |    7 +++++++\n Documentation/git-push.txt      |    6 ++++++\n builtin-fetch.c                 |   24 ++++++++++++++++++------\n builtin-log.c                   |   34 ++++++++++++++++++++++++++++++++++\n builtin-push.c                  |   12 +++++++++---\n builtin.h                       |    2 ++\n transport.c                     |   39 +++++++++++++++++++++++++++------------\n transport.h                     |    2 +-\n 8 files changed, 104 insertions(+), 22 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex d313795..2e66d5e 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -27,6 +27,13 @@\n \tfetches is a descendant of `<lbranch>`.  This option\n \toverrides that check.\n \n+--summary::\n+\tPrint a one-line summary of each commit fetched.\n+\n+--summary=<n>::\n+\tLike --summary, but with a limit of <n> commits per ref.\n+\n+\n ifdef::git-pull[]\n --no-tags::\n endif::git-pull[]\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 2653388..803fe36 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -85,6 +85,12 @@ nor in any Push line of the corresponding remotes file---see below).\n --dry-run::\n \tDo everything except actually send the updates.\n \n+--summary::\n+\tPrint a one-line summary of each commit pushed.\n+\n+--summary=<n>::\n+\tLike --summary, but with a limit of <n> commits per ref.\n+\n --porcelain::\n \tProduce machine-readable output.  The output status line for each ref\n \twill be tab-separated and sent to stdout instead of stderr.  The full\ndiff --git a/builtin-fetch.c b/builtin-fetch.c\nindex cd5eb9a..c98d06b 100644\n--- a/builtin-fetch.c\n+++ b/builtin-fetch.c\n@@ -29,6 +29,7 @@ static const char *depth;\n static const char *upload_pack;\n static struct strbuf default_rla = STRBUF_INIT;\n static struct transport *transport;\n+static int summary;\n \n static struct option builtin_fetch_options[] = {\n \tOPT__VERBOSITY(&verbosity),\n@@ -47,6 +48,9 @@ static struct option builtin_fetch_options[] = {\n \t\t    \"allow updating of HEAD ref\"),\n \tOPT_STRING(0, \"depth\", &depth, \"DEPTH\",\n \t\t   \"deepen history of shallow clone\"),\n+\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] fetched commits\",\n+\t  PARSE_OPT_OPTARG, NULL, 20\n+\t},\n \tOPT_END()\n };\n \n@@ -197,7 +201,8 @@ static int s_update_ref(const char *action,\n \n static int update_local_ref(struct ref *ref,\n \t\t\t    const char *remote,\n-\t\t\t    char *display)\n+\t\t\t    char *display,\n+\t\t\t    char *quickref)\n {\n \tstruct commit *current = NULL, *updated;\n \tenum object_type type;\n@@ -260,11 +265,12 @@ static int update_local_ref(struct ref *ref,\n \t\tsprintf(display, \"%c %-*s %-*s -> %s%s\", r ? '!' : '*',\n \t\t\tSUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,\n \t\t\tr ? \"  (unable to update local ref)\" : \"\");\n+\t\tif (!r)\n+\t\t\tstrcpy(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));\n \t\treturn r;\n \t}\n \n \tif (in_merge_bases(current, &updated, 1)) {\n-\t\tchar quickref[83];\n \t\tint r;\n \t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n \t\tstrcat(quickref, \"..\");\n@@ -275,7 +281,6 @@ static int update_local_ref(struct ref *ref,\n \t\t\tpretty_ref, r ? \"  (unable to update local ref)\" : \"\");\n \t\treturn r;\n \t} else if (force || ref->force) {\n-\t\tchar quickref[84];\n \t\tint r;\n \t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n \t\tstrcat(quickref, \"...\");\n@@ -301,6 +306,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \tstruct commit *commit;\n \tint url_len, i, note_len, shown_url = 0, rc = 0;\n \tchar note[1024];\n+\tchar quickref[84];\n \tconst char *what, *kind;\n \tstruct ref *rm;\n \tchar *url, *filename = git_path(\"FETCH_HEAD\");\n@@ -373,12 +379,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\t\tfputc(url[i], fp);\n \t\tfputc('\\n', fp);\n \n-\t\tif (ref)\n-\t\t\trc |= update_local_ref(ref, what, note);\n-\t\telse\n+\t\tif (ref) {\n+\t\t\t*quickref = 0;\n+\t\t\trc |= update_local_ref(ref, what, note, quickref);\n+\t\t} else {\n+\t\t\tstrcpy(quickref, find_unique_abbrev(rm->old_sha1, DEFAULT_ABBREV));\n \t\t\tsprintf(note, \"* %-*s %-*s -> FETCH_HEAD\",\n \t\t\t\tSUMMARY_WIDTH, *kind ? kind : \"branch\",\n \t\t\t\t REFCOL_WIDTH, *what ? what : \"HEAD\");\n+\t\t}\n \t\tif (*note) {\n \t\t\tif (verbosity >= 0 && !shown_url) {\n \t\t\t\tfprintf(stderr, \"From %.*s\\n\",\n@@ -388,6 +397,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\tif (verbosity >= 0)\n \t\t\t\tfprintf(stderr, \" %s\\n\", note);\n \t\t}\n+\t\tif (summary && quickref[0])\n+\t\t\tprint_summary_for_push_or_fetch(quickref, summary);\n+\n \t}\n \tfree(url);\n \tfclose(fp);\ndiff --git a/builtin-log.c b/builtin-log.c\nindex 0c2fa0a..e25285b 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -1293,3 +1293,37 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \tfree_patch_ids(&ids);\n \treturn 0;\n }\n+\n+\n+void print_summary_for_push_or_fetch(const char *quickref, int limit)\n+{\n+\tstruct rev_info rev;\n+\tint i, max;\n+\tstruct object *obj;\n+\tstruct commit *commit;\n+\n+\tmax = get_max_object_index();\n+\tfor (i = 0; i < max; i++)  {\n+\t\tobj = get_indexed_object(i);\n+\t\tif (obj)\n+\t\t\tobj->flags &= ~ALL_REV_FLAGS;\n+\t}\n+\n+\tinit_revisions(&rev, NULL);\n+\trev.prune = 0;\n+\tassert(!handle_revision_arg(quickref, &rev, 0, 1));\n+\tassert(!prepare_revision_walk(&rev));\n+\n+\twhile ((commit = get_revision(&rev)) != NULL) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tif (limit == 0) {\n+\t\t\tfprintf(stderr, \"    ...\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\t\tformat_commit_message(commit, \"    %m %h %s\\n\", &buf, 0);\n+\t\tfputs(buf.buf, stderr);\n+\t\tstrbuf_release(&buf);\n+\t\tlimit--;\n+\t}\n+\tfputs(\"\\n\", stderr);\n+}\ndiff --git a/builtin-push.c b/builtin-push.c\nindex 0a0297f..d38f9a8 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -113,7 +113,7 @@ static void setup_default_push_refspecs(void)\n \t}\n }\n \n-static int do_push(const char *repo, int flags)\n+static int do_push(const char *repo, int flags, int summary)\n {\n \tint i, errs;\n \tstruct remote *remote = remote_get(repo);\n@@ -173,7 +173,7 @@ static int do_push(const char *repo, int flags)\n \n \t\tif (flags & TRANSPORT_PUSH_VERBOSE)\n \t\t\tfprintf(stderr, \"Pushing to %s\\n\", url[i]);\n-\t\terr = transport_push(transport, refspec_nr, refspec, flags);\n+\t\terr = transport_push(transport, refspec_nr, refspec, flags, summary);\n \t\terr |= transport_disconnect(transport);\n \n \t\tif (!err)\n@@ -192,6 +192,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \tint rc;\n \tconst char *repo = NULL;\t/* default repository */\n \n+\tint summary = 0;\n+\n \tstruct option options[] = {\n \t\tOPT_BIT('v', \"verbose\", &flags, \"be verbose\", TRANSPORT_PUSH_VERBOSE),\n \t\tOPT_STRING( 0 , \"repo\", &repo, \"repository\", \"repository\"),\n@@ -205,6 +207,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOLEAN( 0 , \"thin\", &thin, \"use thin pack\"),\n \t\tOPT_STRING( 0 , \"receive-pack\", &receivepack, \"receive-pack\", \"receive pack program\"),\n \t\tOPT_STRING( 0 , \"exec\", &receivepack, \"receive-pack\", \"receive pack program\"),\n+\t\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] pushed commits\",\n+\t\t  PARSE_OPT_OPTARG, NULL, 20\n+\t\t},\n+\n \t\tOPT_END()\n \t};\n \n@@ -218,7 +224,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tset_refspecs(argv + 1, argc - 1);\n \t}\n \n-\trc = do_push(repo, flags);\n+\trc = do_push(repo, flags, summary);\n \tif (rc == -1)\n \t\tusage_with_options(push_usage, options);\n \telse\ndiff --git a/builtin.h b/builtin.h\nindex 20427d2..5aea3a3 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -113,4 +113,6 @@ extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);\n extern int cmd_show_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);\n \n+extern void print_summary_for_push_or_fetch(const char *quickref, int limit);\n+\n #endif\ndiff --git a/transport.c b/transport.c\nindex de0d587..80105ae 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -11,6 +11,7 @@\n #include \"bundle.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"builtin.h\"\n \n /* rsync support */\n \n@@ -750,17 +751,20 @@ static const char *status_abbrev(unsigned char sha1[20])\n \treturn find_unique_abbrev(sha1, DEFAULT_ABBREV);\n }\n \n-static void print_ok_ref_status(struct ref *ref, int porcelain)\n+static void print_ok_ref_status(struct ref *ref, int porcelain, int summary)\n {\n+\tchar quickref[84];\n+\tint summary_impossible = 0;\n+\n \tif (ref->deletion)\n \t\tprint_ref_status('-', \"[deleted]\", ref, NULL, NULL, porcelain);\n-\telse if (is_null_sha1(ref->old_sha1))\n+\telse if (is_null_sha1(ref->old_sha1)) {\n \t\tprint_ref_status('*',\n \t\t\t(!prefixcmp(ref->name, \"refs/tags/\") ? \"[new tag]\" :\n \t\t\t\"[new branch]\"),\n \t\t\tref, ref->peer_ref, NULL, porcelain);\n-\telse {\n-\t\tchar quickref[84];\n+\t\tstrcpy(quickref, status_abbrev(ref->new_sha1));\n+\t} else {\n \t\tchar type;\n \t\tconst char *msg;\n \n@@ -769,6 +773,8 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \t\t\tstrcat(quickref, \"...\");\n \t\t\ttype = '+';\n \t\t\tmsg = \"forced update\";\n+\t\t\tif (!lookup_commit_reference_gently(ref->old_sha1, 1))\n+\t\t\t\tsummary_impossible = 1;\n \t\t} else {\n \t\t\tstrcat(quickref, \"..\");\n \t\t\ttype = ' ';\n@@ -778,9 +784,17 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \n \t\tprint_ref_status(type, quickref, ref, ref->peer_ref, msg, porcelain);\n \t}\n+\n+\tif (summary) {\n+\t\tif (summary_impossible) {\n+\t\t\tfprintf(stderr, \"    %s is unavailable\\n\", status_abbrev(ref->old_sha1));\n+\t\t} else {\n+\t\t\tprint_summary_for_push_or_fetch(quickref, summary);\n+\t\t}\n+\t}\n }\n \n-static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)\n+static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain, int summary)\n {\n \tif (!count)\n \t\tfprintf(stderr, \"To %s\\n\", dest);\n@@ -812,7 +826,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n \t\t\t\t\t\t \"remote failed to report status\", porcelain);\n \t\tbreak;\n \tcase REF_STATUS_OK:\n-\t\tprint_ok_ref_status(ref, porcelain);\n+\t\tprint_ok_ref_status(ref, porcelain, summary);\n \t\tbreak;\n \t}\n \n@@ -820,7 +834,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n }\n \n static void print_push_status(const char *dest, struct ref *refs,\n-\t\t\t\t\t\t\t  int verbose, int porcelain)\n+\t\t\t\t\t\t\t  int verbose, int porcelain, int summary)\n {\n \tstruct ref *ref;\n \tint n = 0;\n@@ -828,18 +842,18 @@ static void print_push_status(const char *dest, struct ref *refs,\n \tif (verbose) {\n \t\tfor (ref = refs; ref; ref = ref->next)\n \t\t\tif (ref->status == REF_STATUS_UPTODATE)\n-\t\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \t}\n \n \tfor (ref = refs; ref; ref = ref->next)\n \t\tif (ref->status == REF_STATUS_OK)\n-\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \n \tfor (ref = refs; ref; ref = ref->next) {\n \t\tif (ref->status != REF_STATUS_NONE &&\n \t\t    ref->status != REF_STATUS_UPTODATE &&\n \t\t    ref->status != REF_STATUS_OK)\n-\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \t}\n }\n \n@@ -997,7 +1011,8 @@ int transport_set_option(struct transport *transport,\n }\n \n int transport_push(struct transport *transport,\n-\t\t   int refspec_nr, const char **refspec, int flags)\n+\t\t\t\t   int refspec_nr, const char **refspec,\n+\t\t\t\t   int flags, int summary)\n {\n \tverify_remote_names(refspec_nr, refspec);\n \n@@ -1024,7 +1039,7 @@ int transport_push(struct transport *transport,\n \n \t\tret = transport->push_refs(transport, remote_refs, flags);\n \n-\t\tprint_push_status(transport->url, remote_refs, verbose | porcelain, porcelain);\n+\t\tprint_push_status(transport->url, remote_refs, verbose | porcelain, porcelain, summary);\n \n \t\tif (!(flags & TRANSPORT_PUSH_DRY_RUN)) {\n \t\t\tstruct ref *ref;\ndiff --git a/transport.h b/transport.h\nindex 51b5397..360051e 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -68,7 +68,7 @@ int transport_set_option(struct transport *transport, const char *name,\n \t\t\t const char *value);\n \n int transport_push(struct transport *connection,\n-\t\t   int refspec_nr, const char **refspec, int flags);\n+\t\t\t\t   int refspec_nr, const char **refspec, int flags, int summary);\n \n const struct ref *transport_get_remote_refs(struct transport *transport);\n \n-- \n1.6.3.3.409.g4a08\n"},{"id":"117713","messageId":"h35bda$kgv$1@ger.gmane.org","threadId":"20011","inReplyTo":"20090707015948.GA525@cthulhu","subject":"Re: [PATCH v3] add --summary option to git-push and git-fetch","fromName":"Larry D'Anna","fromEmail":"larry@elder-gods.org","sentAt":"2009-07-09T18:03:22Z","receivedAt":"2009-07-09T18:03:22Z","isPatch":true,"sender":{"key":"larry@elder-gods.org","avatar":"https://avatars.githubusercontent.com/u/3013304?v=4"},"body":"\nThis patch has a bug in it that causes it to randomly segfault, so don't bother\nwith it.  I'll send in a fixed version when I figure out the cause.\n\n     --larry\n"},{"id":"117743","messageId":"20090710022415.GA27274@cthulhu","threadId":"20011","inReplyTo":"h35bda$kgv$1@ger.gmane.org","subject":"[PATCH v4] add --summary option to git-push and git-fetch","fromName":"Larry D'Anna","fromEmail":"larry@elder-gods.org","sentAt":"2009-07-10T02:24:15Z","receivedAt":"2009-07-10T02:24:15Z","isPatch":true,"sender":{"key":"larry@elder-gods.org","avatar":"https://avatars.githubusercontent.com/u/3013304?v=4"},"body":"--summary will cause git-push to output a one-line of each commit pushed.\n--summary=n will display at most n commits for each ref pushed.\n\n$ git push --dry-run --summary origin :\nTo /home/larry/gitsandbox/a\n   80f0e50..5593a38  master -> master\n    > 5593a38 foo\n    > 81c03f8 bar\n\nFetch works the same way.\n\nSigned-off-by: Larry D'Anna <larry@elder-gods.org>\n---\n\n Changes since last version: \n\n * fixed the segfalt bug.  commit->buffer was NULL.\n\n Documentation/fetch-options.txt |    7 ++++++\n Documentation/git-push.txt      |    6 +++++\n builtin-fetch.c                 |   24 ++++++++++++++++-----\n builtin-log.c                   |   42 +++++++++++++++++++++++++++++++++++++++\n builtin-push.c                  |   12 ++++++++--\n builtin.h                       |    2 +\n transport.c                     |   39 +++++++++++++++++++++++++-----------\n transport.h                     |    2 +-\n 8 files changed, 112 insertions(+), 22 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex d313795..2e66d5e 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -27,6 +27,13 @@\n \tfetches is a descendant of `<lbranch>`.  This option\n \toverrides that check.\n \n+--summary::\n+\tPrint a one-line summary of each commit fetched.\n+\n+--summary=<n>::\n+\tLike --summary, but with a limit of <n> commits per ref.\n+\n+\n ifdef::git-pull[]\n --no-tags::\n endif::git-pull[]\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 2653388..803fe36 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -85,6 +85,12 @@ nor in any Push line of the corresponding remotes file---see below).\n --dry-run::\n \tDo everything except actually send the updates.\n \n+--summary::\n+\tPrint a one-line summary of each commit pushed.\n+\n+--summary=<n>::\n+\tLike --summary, but with a limit of <n> commits per ref.\n+\n --porcelain::\n \tProduce machine-readable output.  The output status line for each ref\n \twill be tab-separated and sent to stdout instead of stderr.  The full\ndiff --git a/builtin-fetch.c b/builtin-fetch.c\nindex cd5eb9a..c98d06b 100644\n--- a/builtin-fetch.c\n+++ b/builtin-fetch.c\n@@ -29,6 +29,7 @@ static const char *depth;\n static const char *upload_pack;\n static struct strbuf default_rla = STRBUF_INIT;\n static struct transport *transport;\n+static int summary;\n \n static struct option builtin_fetch_options[] = {\n \tOPT__VERBOSITY(&verbosity),\n@@ -47,6 +48,9 @@ static struct option builtin_fetch_options[] = {\n \t\t    \"allow updating of HEAD ref\"),\n \tOPT_STRING(0, \"depth\", &depth, \"DEPTH\",\n \t\t   \"deepen history of shallow clone\"),\n+\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] fetched commits\",\n+\t  PARSE_OPT_OPTARG, NULL, 20\n+\t},\n \tOPT_END()\n };\n \n@@ -197,7 +201,8 @@ static int s_update_ref(const char *action,\n \n static int update_local_ref(struct ref *ref,\n \t\t\t    const char *remote,\n-\t\t\t    char *display)\n+\t\t\t    char *display,\n+\t\t\t    char *quickref)\n {\n \tstruct commit *current = NULL, *updated;\n \tenum object_type type;\n@@ -260,11 +265,12 @@ static int update_local_ref(struct ref *ref,\n \t\tsprintf(display, \"%c %-*s %-*s -> %s%s\", r ? '!' : '*',\n \t\t\tSUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,\n \t\t\tr ? \"  (unable to update local ref)\" : \"\");\n+\t\tif (!r)\n+\t\t\tstrcpy(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));\n \t\treturn r;\n \t}\n \n \tif (in_merge_bases(current, &updated, 1)) {\n-\t\tchar quickref[83];\n \t\tint r;\n \t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n \t\tstrcat(quickref, \"..\");\n@@ -275,7 +281,6 @@ static int update_local_ref(struct ref *ref,\n \t\t\tpretty_ref, r ? \"  (unable to update local ref)\" : \"\");\n \t\treturn r;\n \t} else if (force || ref->force) {\n-\t\tchar quickref[84];\n \t\tint r;\n \t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n \t\tstrcat(quickref, \"...\");\n@@ -301,6 +306,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \tstruct commit *commit;\n \tint url_len, i, note_len, shown_url = 0, rc = 0;\n \tchar note[1024];\n+\tchar quickref[84];\n \tconst char *what, *kind;\n \tstruct ref *rm;\n \tchar *url, *filename = git_path(\"FETCH_HEAD\");\n@@ -373,12 +379,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\t\tfputc(url[i], fp);\n \t\tfputc('\\n', fp);\n \n-\t\tif (ref)\n-\t\t\trc |= update_local_ref(ref, what, note);\n-\t\telse\n+\t\tif (ref) {\n+\t\t\t*quickref = 0;\n+\t\t\trc |= update_local_ref(ref, what, note, quickref);\n+\t\t} else {\n+\t\t\tstrcpy(quickref, find_unique_abbrev(rm->old_sha1, DEFAULT_ABBREV));\n \t\t\tsprintf(note, \"* %-*s %-*s -> FETCH_HEAD\",\n \t\t\t\tSUMMARY_WIDTH, *kind ? kind : \"branch\",\n \t\t\t\t REFCOL_WIDTH, *what ? what : \"HEAD\");\n+\t\t}\n \t\tif (*note) {\n \t\t\tif (verbosity >= 0 && !shown_url) {\n \t\t\t\tfprintf(stderr, \"From %.*s\\n\",\n@@ -388,6 +397,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\tif (verbosity >= 0)\n \t\t\t\tfprintf(stderr, \" %s\\n\", note);\n \t\t}\n+\t\tif (summary && quickref[0])\n+\t\t\tprint_summary_for_push_or_fetch(quickref, summary);\n+\n \t}\n \tfree(url);\n \tfclose(fp);\ndiff --git a/builtin-log.c b/builtin-log.c\nindex 0c2fa0a..a09670c 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -1293,3 +1293,45 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \tfree_patch_ids(&ids);\n \treturn 0;\n }\n+\n+\n+void print_summary_for_push_or_fetch(const char *quickref, int limit)\n+{\n+\tstruct rev_info rev;\n+\tint i, max;\n+\tstruct object *obj;\n+\tstruct commit *commit;\n+\n+\tmax = get_max_object_index();\n+\tfor (i = 0; i < max; i++)  {\n+\t\tobj = get_indexed_object(i);\n+\t\tif (obj)\n+\t\t\tobj->flags &= ~ALL_REV_FLAGS;\n+\t}\n+\n+\tinit_revisions(&rev, NULL);\n+\trev.prune = 0;\n+\tassert(!handle_revision_arg(quickref, &rev, 0, 1));\n+\tassert(!prepare_revision_walk(&rev));\n+\n+\twhile ((commit = get_revision(&rev)) != NULL) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tif (limit == 0) {\n+\t\t\tfprintf(stderr, \"    ...\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!commit->buffer) {\n+\t\t\tenum object_type type;\n+\t\t\tunsigned long size;\n+\t\t\tcommit->buffer =\n+\t\t\t\tread_sha1_file(commit->object.sha1, &type, &size);\n+\t\t\tif (!commit->buffer)\n+\t\t\t\tdie(\"Cannot read commit %s\", sha1_to_hex(commit->object.sha1));\n+\t\t}\n+\t\tformat_commit_message(commit, \"    %m %h %s\\n\", &buf, 0);\n+\t\tfputs(buf.buf, stderr);\n+\t\tstrbuf_release(&buf);\n+\t\tlimit--;\n+\t}\n+\tfputs(\"\\n\", stderr);\n+}\ndiff --git a/builtin-push.c b/builtin-push.c\nindex 0a0297f..d38f9a8 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -113,7 +113,7 @@ static void setup_default_push_refspecs(void)\n \t}\n }\n \n-static int do_push(const char *repo, int flags)\n+static int do_push(const char *repo, int flags, int summary)\n {\n \tint i, errs;\n \tstruct remote *remote = remote_get(repo);\n@@ -173,7 +173,7 @@ static int do_push(const char *repo, int flags)\n \n \t\tif (flags & TRANSPORT_PUSH_VERBOSE)\n \t\t\tfprintf(stderr, \"Pushing to %s\\n\", url[i]);\n-\t\terr = transport_push(transport, refspec_nr, refspec, flags);\n+\t\terr = transport_push(transport, refspec_nr, refspec, flags, summary);\n \t\terr |= transport_disconnect(transport);\n \n \t\tif (!err)\n@@ -192,6 +192,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \tint rc;\n \tconst char *repo = NULL;\t/* default repository */\n \n+\tint summary = 0;\n+\n \tstruct option options[] = {\n \t\tOPT_BIT('v', \"verbose\", &flags, \"be verbose\", TRANSPORT_PUSH_VERBOSE),\n \t\tOPT_STRING( 0 , \"repo\", &repo, \"repository\", \"repository\"),\n@@ -205,6 +207,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOLEAN( 0 , \"thin\", &thin, \"use thin pack\"),\n \t\tOPT_STRING( 0 , \"receive-pack\", &receivepack, \"receive-pack\", \"receive pack program\"),\n \t\tOPT_STRING( 0 , \"exec\", &receivepack, \"receive-pack\", \"receive pack program\"),\n+\t\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] pushed commits\",\n+\t\t  PARSE_OPT_OPTARG, NULL, 20\n+\t\t},\n+\n \t\tOPT_END()\n \t};\n \n@@ -218,7 +224,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tset_refspecs(argv + 1, argc - 1);\n \t}\n \n-\trc = do_push(repo, flags);\n+\trc = do_push(repo, flags, summary);\n \tif (rc == -1)\n \t\tusage_with_options(push_usage, options);\n \telse\ndiff --git a/builtin.h b/builtin.h\nindex 20427d2..5aea3a3 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -113,4 +113,6 @@ extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);\n extern int cmd_show_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);\n \n+extern void print_summary_for_push_or_fetch(const char *quickref, int limit);\n+\n #endif\ndiff --git a/transport.c b/transport.c\nindex de0d587..80105ae 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -11,6 +11,7 @@\n #include \"bundle.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"builtin.h\"\n \n /* rsync support */\n \n@@ -750,17 +751,20 @@ static const char *status_abbrev(unsigned char sha1[20])\n \treturn find_unique_abbrev(sha1, DEFAULT_ABBREV);\n }\n \n-static void print_ok_ref_status(struct ref *ref, int porcelain)\n+static void print_ok_ref_status(struct ref *ref, int porcelain, int summary)\n {\n+\tchar quickref[84];\n+\tint summary_impossible = 0;\n+\n \tif (ref->deletion)\n \t\tprint_ref_status('-', \"[deleted]\", ref, NULL, NULL, porcelain);\n-\telse if (is_null_sha1(ref->old_sha1))\n+\telse if (is_null_sha1(ref->old_sha1)) {\n \t\tprint_ref_status('*',\n \t\t\t(!prefixcmp(ref->name, \"refs/tags/\") ? \"[new tag]\" :\n \t\t\t\"[new branch]\"),\n \t\t\tref, ref->peer_ref, NULL, porcelain);\n-\telse {\n-\t\tchar quickref[84];\n+\t\tstrcpy(quickref, status_abbrev(ref->new_sha1));\n+\t} else {\n \t\tchar type;\n \t\tconst char *msg;\n \n@@ -769,6 +773,8 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \t\t\tstrcat(quickref, \"...\");\n \t\t\ttype = '+';\n \t\t\tmsg = \"forced update\";\n+\t\t\tif (!lookup_commit_reference_gently(ref->old_sha1, 1))\n+\t\t\t\tsummary_impossible = 1;\n \t\t} else {\n \t\t\tstrcat(quickref, \"..\");\n \t\t\ttype = ' ';\n@@ -778,9 +784,17 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \n \t\tprint_ref_status(type, quickref, ref, ref->peer_ref, msg, porcelain);\n \t}\n+\n+\tif (summary) {\n+\t\tif (summary_impossible) {\n+\t\t\tfprintf(stderr, \"    %s is unavailable\\n\", status_abbrev(ref->old_sha1));\n+\t\t} else {\n+\t\t\tprint_summary_for_push_or_fetch(quickref, summary);\n+\t\t}\n+\t}\n }\n \n-static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)\n+static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain, int summary)\n {\n \tif (!count)\n \t\tfprintf(stderr, \"To %s\\n\", dest);\n@@ -812,7 +826,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n \t\t\t\t\t\t \"remote failed to report status\", porcelain);\n \t\tbreak;\n \tcase REF_STATUS_OK:\n-\t\tprint_ok_ref_status(ref, porcelain);\n+\t\tprint_ok_ref_status(ref, porcelain, summary);\n \t\tbreak;\n \t}\n \n@@ -820,7 +834,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n }\n \n static void print_push_status(const char *dest, struct ref *refs,\n-\t\t\t\t\t\t\t  int verbose, int porcelain)\n+\t\t\t\t\t\t\t  int verbose, int porcelain, int summary)\n {\n \tstruct ref *ref;\n \tint n = 0;\n@@ -828,18 +842,18 @@ static void print_push_status(const char *dest, struct ref *refs,\n \tif (verbose) {\n \t\tfor (ref = refs; ref; ref = ref->next)\n \t\t\tif (ref->status == REF_STATUS_UPTODATE)\n-\t\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \t}\n \n \tfor (ref = refs; ref; ref = ref->next)\n \t\tif (ref->status == REF_STATUS_OK)\n-\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \n \tfor (ref = refs; ref; ref = ref->next) {\n \t\tif (ref->status != REF_STATUS_NONE &&\n \t\t    ref->status != REF_STATUS_UPTODATE &&\n \t\t    ref->status != REF_STATUS_OK)\n-\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \t}\n }\n \n@@ -997,7 +1011,8 @@ int transport_set_option(struct transport *transport,\n }\n \n int transport_push(struct transport *transport,\n-\t\t   int refspec_nr, const char **refspec, int flags)\n+\t\t\t\t   int refspec_nr, const char **refspec,\n+\t\t\t\t   int flags, int summary)\n {\n \tverify_remote_names(refspec_nr, refspec);\n \n@@ -1024,7 +1039,7 @@ int transport_push(struct transport *transport,\n \n \t\tret = transport->push_refs(transport, remote_refs, flags);\n \n-\t\tprint_push_status(transport->url, remote_refs, verbose | porcelain, porcelain);\n+\t\tprint_push_status(transport->url, remote_refs, verbose | porcelain, porcelain, summary);\n \n \t\tif (!(flags & TRANSPORT_PUSH_DRY_RUN)) {\n \t\t\tstruct ref *ref;\ndiff --git a/transport.h b/transport.h\nindex 51b5397..360051e 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -68,7 +68,7 @@ int transport_set_option(struct transport *transport, const char *name,\n \t\t\t const char *value);\n \n int transport_push(struct transport *connection,\n-\t\t   int refspec_nr, const char **refspec, int flags);\n+\t\t\t\t   int refspec_nr, const char **refspec, int flags, int summary);\n \n const struct ref *transport_get_remote_refs(struct transport *transport);\n \n-- \n1.6.3.3.415.gbe1e\n"},{"id":"117755","messageId":"4A56EEBA.3070806@gmail.com","threadId":"20011","inReplyTo":"20090710022415.GA27274@cthulhu","subject":"Re: [PATCH v4] add --summary option to git-push and git-fetch","fromName":"Stephen Boyd","fromEmail":"bebarino@gmail.com","sentAt":"2009-07-10T07:33:14Z","receivedAt":"2009-07-10T07:33:14Z","isPatch":true,"sender":{"key":"bebarino@gmail.com","avatar":"https://avatars.githubusercontent.com/u/38832?v=4"},"body":"Larry D'Anna wrote:\n> --summary will cause git-push to output a one-line of each commit pushed.\n> --summary=n will display at most n commits for each ref pushed.\n>\n> $ git push --dry-run --summary origin :\n> To /home/larry/gitsandbox/a\n>    80f0e50..5593a38  master -> master\n>     > 5593a38 foo\n>     > 81c03f8 bar\n>\n> Fetch works the same way.\n>\n> Signed-off-by: Larry D'Anna <larry@elder-gods.org>\n> ---\n>\n>  Changes since last version: \n>\n>  * fixed the segfalt bug.  commit->buffer was NULL.\n>   \n\nMaybe adding a few tests to exercise this new option will give reviewers\na better assurance you've squashed the bugs in previous rounds?\n"},{"id":"117829","messageId":"20090711174156.GA17154@cthulhu","threadId":"20011","inReplyTo":"4A56EEBA.3070806@gmail.com","subject":"[PATCH v4] add --summary option to git-push and git-fetch","fromName":"Larry D'Anna","fromEmail":"larry@elder-gods.org","sentAt":"2009-07-11T17:41:56Z","receivedAt":"2009-07-11T17:41:56Z","isPatch":true,"sender":{"key":"larry@elder-gods.org","avatar":"https://avatars.githubusercontent.com/u/3013304?v=4"},"body":"--summary will cause git-push to output a one-line of each commit pushed.\n--summary=n will display at most n commits for each ref pushed.\n\n$ git push --dry-run --summary origin :\nTo /home/larry/gitsandbox/a\n   80f0e50..5593a38  master -> master\n    > 5593a38 foo\n    > 81c03f8 bar\n\nFetch works the same way.\n\nSigned-off-by: Larry D'Anna <larry@elder-gods.org>\n---\n \n Changes sicne last version of this patch: \n\n    * added some tests\n\n Documentation/fetch-options.txt |    7 ++++\n Documentation/git-push.txt      |    6 +++\n builtin-fetch.c                 |   24 +++++++++---\n builtin-log.c                   |   42 +++++++++++++++++++++\n builtin-push.c                  |   12 +++++--\n builtin.h                       |    2 +\n t/t5516-fetch-push.sh           |   77 +++++++++++++++++++++++++++++++++++++++\n transport.c                     |   39 ++++++++++++++------\n transport.h                     |    2 +-\n 9 files changed, 189 insertions(+), 22 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex d313795..2e66d5e 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -27,6 +27,13 @@\n \tfetches is a descendant of `<lbranch>`.  This option\n \toverrides that check.\n \n+--summary::\n+\tPrint a one-line summary of each commit fetched.\n+\n+--summary=<n>::\n+\tLike --summary, but with a limit of <n> commits per ref.\n+\n+\n ifdef::git-pull[]\n --no-tags::\n endif::git-pull[]\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 2653388..803fe36 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -85,6 +85,12 @@ nor in any Push line of the corresponding remotes file---see below).\n --dry-run::\n \tDo everything except actually send the updates.\n \n+--summary::\n+\tPrint a one-line summary of each commit pushed.\n+\n+--summary=<n>::\n+\tLike --summary, but with a limit of <n> commits per ref.\n+\n --porcelain::\n \tProduce machine-readable output.  The output status line for each ref\n \twill be tab-separated and sent to stdout instead of stderr.  The full\ndiff --git a/builtin-fetch.c b/builtin-fetch.c\nindex cd5eb9a..c98d06b 100644\n--- a/builtin-fetch.c\n+++ b/builtin-fetch.c\n@@ -29,6 +29,7 @@ static const char *depth;\n static const char *upload_pack;\n static struct strbuf default_rla = STRBUF_INIT;\n static struct transport *transport;\n+static int summary;\n \n static struct option builtin_fetch_options[] = {\n \tOPT__VERBOSITY(&verbosity),\n@@ -47,6 +48,9 @@ static struct option builtin_fetch_options[] = {\n \t\t    \"allow updating of HEAD ref\"),\n \tOPT_STRING(0, \"depth\", &depth, \"DEPTH\",\n \t\t   \"deepen history of shallow clone\"),\n+\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] fetched commits\",\n+\t  PARSE_OPT_OPTARG, NULL, 20\n+\t},\n \tOPT_END()\n };\n \n@@ -197,7 +201,8 @@ static int s_update_ref(const char *action,\n \n static int update_local_ref(struct ref *ref,\n \t\t\t    const char *remote,\n-\t\t\t    char *display)\n+\t\t\t    char *display,\n+\t\t\t    char *quickref)\n {\n \tstruct commit *current = NULL, *updated;\n \tenum object_type type;\n@@ -260,11 +265,12 @@ static int update_local_ref(struct ref *ref,\n \t\tsprintf(display, \"%c %-*s %-*s -> %s%s\", r ? '!' : '*',\n \t\t\tSUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,\n \t\t\tr ? \"  (unable to update local ref)\" : \"\");\n+\t\tif (!r)\n+\t\t\tstrcpy(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));\n \t\treturn r;\n \t}\n \n \tif (in_merge_bases(current, &updated, 1)) {\n-\t\tchar quickref[83];\n \t\tint r;\n \t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n \t\tstrcat(quickref, \"..\");\n@@ -275,7 +281,6 @@ static int update_local_ref(struct ref *ref,\n \t\t\tpretty_ref, r ? \"  (unable to update local ref)\" : \"\");\n \t\treturn r;\n \t} else if (force || ref->force) {\n-\t\tchar quickref[84];\n \t\tint r;\n \t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n \t\tstrcat(quickref, \"...\");\n@@ -301,6 +306,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \tstruct commit *commit;\n \tint url_len, i, note_len, shown_url = 0, rc = 0;\n \tchar note[1024];\n+\tchar quickref[84];\n \tconst char *what, *kind;\n \tstruct ref *rm;\n \tchar *url, *filename = git_path(\"FETCH_HEAD\");\n@@ -373,12 +379,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\t\tfputc(url[i], fp);\n \t\tfputc('\\n', fp);\n \n-\t\tif (ref)\n-\t\t\trc |= update_local_ref(ref, what, note);\n-\t\telse\n+\t\tif (ref) {\n+\t\t\t*quickref = 0;\n+\t\t\trc |= update_local_ref(ref, what, note, quickref);\n+\t\t} else {\n+\t\t\tstrcpy(quickref, find_unique_abbrev(rm->old_sha1, DEFAULT_ABBREV));\n \t\t\tsprintf(note, \"* %-*s %-*s -> FETCH_HEAD\",\n \t\t\t\tSUMMARY_WIDTH, *kind ? kind : \"branch\",\n \t\t\t\t REFCOL_WIDTH, *what ? what : \"HEAD\");\n+\t\t}\n \t\tif (*note) {\n \t\t\tif (verbosity >= 0 && !shown_url) {\n \t\t\t\tfprintf(stderr, \"From %.*s\\n\",\n@@ -388,6 +397,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\tif (verbosity >= 0)\n \t\t\t\tfprintf(stderr, \" %s\\n\", note);\n \t\t}\n+\t\tif (summary && quickref[0])\n+\t\t\tprint_summary_for_push_or_fetch(quickref, summary);\n+\n \t}\n \tfree(url);\n \tfclose(fp);\ndiff --git a/builtin-log.c b/builtin-log.c\nindex 0c2fa0a..a09670c 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -1293,3 +1293,45 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \tfree_patch_ids(&ids);\n \treturn 0;\n }\n+\n+\n+void print_summary_for_push_or_fetch(const char *quickref, int limit)\n+{\n+\tstruct rev_info rev;\n+\tint i, max;\n+\tstruct object *obj;\n+\tstruct commit *commit;\n+\n+\tmax = get_max_object_index();\n+\tfor (i = 0; i < max; i++)  {\n+\t\tobj = get_indexed_object(i);\n+\t\tif (obj)\n+\t\t\tobj->flags &= ~ALL_REV_FLAGS;\n+\t}\n+\n+\tinit_revisions(&rev, NULL);\n+\trev.prune = 0;\n+\tassert(!handle_revision_arg(quickref, &rev, 0, 1));\n+\tassert(!prepare_revision_walk(&rev));\n+\n+\twhile ((commit = get_revision(&rev)) != NULL) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tif (limit == 0) {\n+\t\t\tfprintf(stderr, \"    ...\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!commit->buffer) {\n+\t\t\tenum object_type type;\n+\t\t\tunsigned long size;\n+\t\t\tcommit->buffer =\n+\t\t\t\tread_sha1_file(commit->object.sha1, &type, &size);\n+\t\t\tif (!commit->buffer)\n+\t\t\t\tdie(\"Cannot read commit %s\", sha1_to_hex(commit->object.sha1));\n+\t\t}\n+\t\tformat_commit_message(commit, \"    %m %h %s\\n\", &buf, 0);\n+\t\tfputs(buf.buf, stderr);\n+\t\tstrbuf_release(&buf);\n+\t\tlimit--;\n+\t}\n+\tfputs(\"\\n\", stderr);\n+}\ndiff --git a/builtin-push.c b/builtin-push.c\nindex 0a0297f..d38f9a8 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -113,7 +113,7 @@ static void setup_default_push_refspecs(void)\n \t}\n }\n \n-static int do_push(const char *repo, int flags)\n+static int do_push(const char *repo, int flags, int summary)\n {\n \tint i, errs;\n \tstruct remote *remote = remote_get(repo);\n@@ -173,7 +173,7 @@ static int do_push(const char *repo, int flags)\n \n \t\tif (flags & TRANSPORT_PUSH_VERBOSE)\n \t\t\tfprintf(stderr, \"Pushing to %s\\n\", url[i]);\n-\t\terr = transport_push(transport, refspec_nr, refspec, flags);\n+\t\terr = transport_push(transport, refspec_nr, refspec, flags, summary);\n \t\terr |= transport_disconnect(transport);\n \n \t\tif (!err)\n@@ -192,6 +192,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \tint rc;\n \tconst char *repo = NULL;\t/* default repository */\n \n+\tint summary = 0;\n+\n \tstruct option options[] = {\n \t\tOPT_BIT('v', \"verbose\", &flags, \"be verbose\", TRANSPORT_PUSH_VERBOSE),\n \t\tOPT_STRING( 0 , \"repo\", &repo, \"repository\", \"repository\"),\n@@ -205,6 +207,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOLEAN( 0 , \"thin\", &thin, \"use thin pack\"),\n \t\tOPT_STRING( 0 , \"receive-pack\", &receivepack, \"receive-pack\", \"receive pack program\"),\n \t\tOPT_STRING( 0 , \"exec\", &receivepack, \"receive-pack\", \"receive pack program\"),\n+\t\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] pushed commits\",\n+\t\t  PARSE_OPT_OPTARG, NULL, 20\n+\t\t},\n+\n \t\tOPT_END()\n \t};\n \n@@ -218,7 +224,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tset_refspecs(argv + 1, argc - 1);\n \t}\n \n-\trc = do_push(repo, flags);\n+\trc = do_push(repo, flags, summary);\n \tif (rc == -1)\n \t\tusage_with_options(push_usage, options);\n \telse\ndiff --git a/builtin.h b/builtin.h\nindex 20427d2..5aea3a3 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -113,4 +113,6 @@ extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);\n extern int cmd_show_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);\n \n+extern void print_summary_for_push_or_fetch(const char *quickref, int limit);\n+\n #endif\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 2d2633f..cae270d 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -90,6 +90,48 @@ test_expect_success 'fetch without wildcard' '\n \t)\n '\n \n+test_expect_success 'fetch --summary' '\n+\tmk_empty &&\n+\t(\n+\t\tcd testrepo &&\n+\t\tgit fetch --summary .. refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l) &&\n+\n+\t\tgrep -E  \"^    > [a-fA-F0-9]+ second$\" stderr &&\n+\t\tgrep -E  \"^    > [a-fA-F0-9]+ repo$\" stderr\n+\t)\n+        '\n+\n+test_expect_success 'fetch --summary forced update' '\n+\tmk_empty &&\n+\t(\n+\t\tcd testrepo &&\n+\t\tgit fetch .. refs/heads/master:refs/remotes/origin/master &&\n+\n+\t\tgit checkout refs/remotes/origin/master^ &&\n+\t\t: >path3 &&\n+\t\tgit add path3 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m third &&\n+\t\tgit update-ref refs/remotes/origin/master HEAD &&\n+\n+\t\tgit fetch .. -f --summary refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l) &&\n+\n+\t\tgrep -E  \"^    < [a-fA-F0-9]+ third$\" stderr &&\n+\t\tgrep -E  \"^    > [a-fA-F0-9]+ second$\" stderr\n+\t)\n+\n+'\n+\n test_expect_success 'fetch with wildcard' '\n \tmk_empty &&\n \t(\n@@ -135,6 +177,41 @@ test_expect_success 'push without wildcard' '\n \t)\n '\n \n+test_expect_success 'push --summary' '\n+\tmk_empty &&\n+\n+\tgit push --summary testrepo refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\t(\n+\t\tcd testrepo &&\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l)\n+\t) &&\n+\n+\tgrep -E  \"^    > [a-fA-F0-9]+ second$\" stderr &&\n+\tgrep -E  \"^    > [a-fA-F0-9]+ repo$\" stderr\n+'\n+\n+test_expect_success 'push --summary forced update' '\n+\tmk_empty &&\n+\n+\tgit push testrepo refs/heads/master:refs/remotes/origin/master &&\n+\n+\tgit checkout master^ &&\n+\t: >path3 &&\n+\tgit add path3 &&\n+\ttest_tick &&\n+\tgit commit -a -m third &&\n+\n+\tgit push --summary -f testrepo HEAD:refs/remotes/origin/master 2>stderr &&\n+\n+\tgrep -E  \"^    < [a-fA-F0-9]+ second$\" stderr &&\n+\tgrep -E  \"^    > [a-fA-F0-9]+ third$\" stderr &&\n+\n+\tgit checkout master\n+'\n+\n test_expect_success 'push with wildcard' '\n \tmk_empty &&\n \ndiff --git a/transport.c b/transport.c\nindex de0d587..80105ae 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -11,6 +11,7 @@\n #include \"bundle.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"builtin.h\"\n \n /* rsync support */\n \n@@ -750,17 +751,20 @@ static const char *status_abbrev(unsigned char sha1[20])\n \treturn find_unique_abbrev(sha1, DEFAULT_ABBREV);\n }\n \n-static void print_ok_ref_status(struct ref *ref, int porcelain)\n+static void print_ok_ref_status(struct ref *ref, int porcelain, int summary)\n {\n+\tchar quickref[84];\n+\tint summary_impossible = 0;\n+\n \tif (ref->deletion)\n \t\tprint_ref_status('-', \"[deleted]\", ref, NULL, NULL, porcelain);\n-\telse if (is_null_sha1(ref->old_sha1))\n+\telse if (is_null_sha1(ref->old_sha1)) {\n \t\tprint_ref_status('*',\n \t\t\t(!prefixcmp(ref->name, \"refs/tags/\") ? \"[new tag]\" :\n \t\t\t\"[new branch]\"),\n \t\t\tref, ref->peer_ref, NULL, porcelain);\n-\telse {\n-\t\tchar quickref[84];\n+\t\tstrcpy(quickref, status_abbrev(ref->new_sha1));\n+\t} else {\n \t\tchar type;\n \t\tconst char *msg;\n \n@@ -769,6 +773,8 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \t\t\tstrcat(quickref, \"...\");\n \t\t\ttype = '+';\n \t\t\tmsg = \"forced update\";\n+\t\t\tif (!lookup_commit_reference_gently(ref->old_sha1, 1))\n+\t\t\t\tsummary_impossible = 1;\n \t\t} else {\n \t\t\tstrcat(quickref, \"..\");\n \t\t\ttype = ' ';\n@@ -778,9 +784,17 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \n \t\tprint_ref_status(type, quickref, ref, ref->peer_ref, msg, porcelain);\n \t}\n+\n+\tif (summary) {\n+\t\tif (summary_impossible) {\n+\t\t\tfprintf(stderr, \"    %s is unavailable\\n\", status_abbrev(ref->old_sha1));\n+\t\t} else {\n+\t\t\tprint_summary_for_push_or_fetch(quickref, summary);\n+\t\t}\n+\t}\n }\n \n-static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)\n+static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain, int summary)\n {\n \tif (!count)\n \t\tfprintf(stderr, \"To %s\\n\", dest);\n@@ -812,7 +826,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n \t\t\t\t\t\t \"remote failed to report status\", porcelain);\n \t\tbreak;\n \tcase REF_STATUS_OK:\n-\t\tprint_ok_ref_status(ref, porcelain);\n+\t\tprint_ok_ref_status(ref, porcelain, summary);\n \t\tbreak;\n \t}\n \n@@ -820,7 +834,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n }\n \n static void print_push_status(const char *dest, struct ref *refs,\n-\t\t\t\t\t\t\t  int verbose, int porcelain)\n+\t\t\t\t\t\t\t  int verbose, int porcelain, int summary)\n {\n \tstruct ref *ref;\n \tint n = 0;\n@@ -828,18 +842,18 @@ static void print_push_status(const char *dest, struct ref *refs,\n \tif (verbose) {\n \t\tfor (ref = refs; ref; ref = ref->next)\n \t\t\tif (ref->status == REF_STATUS_UPTODATE)\n-\t\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \t}\n \n \tfor (ref = refs; ref; ref = ref->next)\n \t\tif (ref->status == REF_STATUS_OK)\n-\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \n \tfor (ref = refs; ref; ref = ref->next) {\n \t\tif (ref->status != REF_STATUS_NONE &&\n \t\t    ref->status != REF_STATUS_UPTODATE &&\n \t\t    ref->status != REF_STATUS_OK)\n-\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \t}\n }\n \n@@ -997,7 +1011,8 @@ int transport_set_option(struct transport *transport,\n }\n \n int transport_push(struct transport *transport,\n-\t\t   int refspec_nr, const char **refspec, int flags)\n+\t\t\t\t   int refspec_nr, const char **refspec,\n+\t\t\t\t   int flags, int summary)\n {\n \tverify_remote_names(refspec_nr, refspec);\n \n@@ -1024,7 +1039,7 @@ int transport_push(struct transport *transport,\n \n \t\tret = transport->push_refs(transport, remote_refs, flags);\n \n-\t\tprint_push_status(transport->url, remote_refs, verbose | porcelain, porcelain);\n+\t\tprint_push_status(transport->url, remote_refs, verbose | porcelain, porcelain, summary);\n \n \t\tif (!(flags & TRANSPORT_PUSH_DRY_RUN)) {\n \t\t\tstruct ref *ref;\ndiff --git a/transport.h b/transport.h\nindex 51b5397..360051e 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -68,7 +68,7 @@ int transport_set_option(struct transport *transport, const char *name,\n \t\t\t const char *value);\n \n int transport_push(struct transport *connection,\n-\t\t   int refspec_nr, const char **refspec, int flags);\n+\t\t\t\t   int refspec_nr, const char **refspec, int flags, int summary);\n \n const struct ref *transport_get_remote_refs(struct transport *transport);\n \n-- \n1.6.3.3.415.gbe1e\n"},{"id":"117830","messageId":"7viqhzm454.fsf@alter.siamese.dyndns.org","threadId":"20011","inReplyTo":"20090711174156.GA17154@cthulhu","subject":"Re: [PATCH v4] add --summary option to git-push and git-fetch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-07-11T19:05:43Z","receivedAt":"2009-07-11T19:05:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Larry D'Anna <larry@elder-gods.org> writes:\n\n> --summary will cause git-push to output a one-line of each commit pushed.\n> --summary=n will display at most n commits for each ref pushed.\n>\n> $ git push --dry-run --summary origin :\n> To /home/larry/gitsandbox/a\n>    80f0e50..5593a38  master -> master\n>     > 5593a38 foo\n>     > 81c03f8 bar\n> Fetch works the same way.\n>\n> Signed-off-by: Larry D'Anna <larry@elder-gods.org>\n> ---\n>  \n>  Changes sicne last version of this patch: \n>\n>     * added some tests\n\nThanks.\n\n> @@ -373,12 +379,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n>  \t\t\t\tfputc(url[i], fp);\n>  \t\tfputc('\\n', fp);\n>  \n> -\t\tif (ref)\n> -\t\t\trc |= update_local_ref(ref, what, note);\n> -\t\telse\n> +\t\tif (ref) {\n> +\t\t\t*quickref = 0;\n> +\t\t\trc |= update_local_ref(ref, what, note, quickref);\n\nMakes me wonder why update_local_ref() does not put that NUL upon entry.\n\n> +void print_summary_for_push_or_fetch(const char *quickref, int limit)\n> +{\n> +\tstruct rev_info rev;\n> +\tint i, max;\n> +\tstruct object *obj;\n> +\tstruct commit *commit;\n> +\n> +\tmax = get_max_object_index();\n> +\tfor (i = 0; i < max; i++)  {\n> +\t\tobj = get_indexed_object(i);\n> +\t\tif (obj)\n> +\t\t\tobj->flags &= ~ALL_REV_FLAGS;\n> +\t}\n\nYuck; this is a horribly heavy sledgehammer.  Couldn't you at least do\nclear_commit_marks() to limit the extent of the damage?\n\n> +\tinit_revisions(&rev, NULL);\n> +\trev.prune = 0;\n> +\tassert(!handle_revision_arg(quickref, &rev, 0, 1));\n> +\tassert(!prepare_revision_walk(&rev));\n> +\n> +\twhile ((commit = get_revision(&rev)) != NULL) {\n> +\t\tstruct strbuf buf = STRBUF_INIT;\n> +\t\tif (limit == 0) {\n> +\t\t\tfprintf(stderr, \"    ...\\n\");\n\nHow would you know, when you asked 20 and you showed 20 here, that there\nis no more to come?\n\n> +\t\t\tbreak;\n> +\t\t}\n\n> +\t\tif (!commit->buffer) {\n> +\t\t\tenum object_type type;\n> +\t\t\tunsigned long size;\n> +\t\t\tcommit->buffer =\n> +\t\t\t\tread_sha1_file(commit->object.sha1, &type, &size);\n> +\t\t\tif (!commit->buffer)\n> +\t\t\t\tdie(\"Cannot read commit %s\", sha1_to_hex(commit->object.sha1));\n> +\t\t}\n> +\t\tformat_commit_message(commit, \"    %m %h %s\\n\", &buf, 0);\n\nHmm, why so many spaces before %m and after %m?\n\n> -static int do_push(const char *repo, int flags)\n> +static int do_push(const char *repo, int flags, int summary)\n\nCouldn't this be just another bit in the flag?  I didn't check but I\nsuspect you wouldn't have to touch the intermediate functions in the call\nchain that way.\n\n> diff --git a/builtin.h b/builtin.h\n> index 20427d2..5aea3a3 100644\n> --- a/builtin.h\n> +++ b/builtin.h\n> @@ -113,4 +113,6 @@ extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);\n>  extern int cmd_show_ref(int argc, const char **argv, const char *prefix);\n>  extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);\n>  \n> +extern void print_summary_for_push_or_fetch(const char *quickref, int limit);\n\nPlease; not at the end, but at the front where all the other command\nhelpers live.  I actually suspect that it might be better to migrate some\npart of builtin-log.c, together with your new helper function, to a new\nfile log.c with accompanying header file log.h, but that could be a\nseparate patch.\n\n> +test_expect_success 'fetch --summary' '\n> +\tmk_empty &&\n> +\t(\n> +\t\tcd testrepo &&\n> +\t\tgit fetch --summary .. refs/heads/master:refs/remotes/origin/master 2>stderr &&\n> +\n> +\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n> +\t\ttest \"z$r\" = \"z$the_commit\" &&\n> +\n> +\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l) &&\n> +\n> +\t\tgrep -E  \"^    > [a-fA-F0-9]+ second$\" stderr &&\n> +\t\tgrep -E  \"^    > [a-fA-F0-9]+ repo$\" stderr\n\nLook at the output from\n\n    $ git grep -n -e 'grep .*-E' -e 'egrep '\n\nbefore applying your patch.  I think we support people with grep that does\nnot know about -E option.\n\n> +test_expect_success 'fetch --summary forced update' '\n> +\tmk_empty &&\n> +\t(\n> ...\n> +\t)\n> +\n> +'\n\nThere are at least two missing combinations. (1) \"fetch --summary\" to\nfetch a new branch, and (2) \"fetch --summary\" does not try segfaulting by\naccessing unavailable information after a failed fetch.\n\nThe same comment applies to the push side of the tests.\n\n> -static void print_ok_ref_status(struct ref *ref, int porcelain)\n> +static void print_ok_ref_status(struct ref *ref, int porcelain, int summary)\n\nThe same comment on \"flags\" applies here and the all the functions in the\ncall chain that adds this extra parameter.  porcelain/summary should be a\nsingle int with two bits used.  It may be cleaner to change \"porcelain\" to\n\"a bit inside flag\" in a separate patch _before_ this one, as a cleanup of\nthe previous \"add --porcelain option to git-push\" patch.\n"},{"id":"133054","messageId":"20100130005948.GA14938@cthulhu","threadId":"20011","inReplyTo":"7viqhzm454.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v4] add --summary option to git-push and git-fetch","fromName":"Larry D'Anna","fromEmail":"larry@elder-gods.org","sentAt":"2010-01-30T00:59:48Z","receivedAt":"2010-01-30T00:59:48Z","isPatch":true,"sender":{"key":"larry@elder-gods.org","avatar":"https://avatars.githubusercontent.com/u/3013304?v=4"},"body":"I know it's been a while but.....\n\n> > @@ -373,12 +379,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n> >  \t\t\t\tfputc(url[i], fp);\n> >  \t\tfputc('\\n', fp);\n> >  \n> > -\t\tif (ref)\n> > -\t\t\trc |= update_local_ref(ref, what, note);\n> > -\t\telse\n> > +\t\tif (ref) {\n> > +\t\t\t*quickref = 0;\n> > +\t\t\trc |= update_local_ref(ref, what, note, quickref);\n> \n> Makes me wonder why update_local_ref() does not put that NUL upon entry.\n\nI'm not sure what you mean.  Could you elaborate?\n\n> > +\tinit_revisions(&rev, NULL);\n> > +\trev.prune = 0;\n> > +\tassert(!handle_revision_arg(quickref, &rev, 0, 1));\n> > +\tassert(!prepare_revision_walk(&rev));\n> > +\n> > +\twhile ((commit = get_revision(&rev)) != NULL) {\n> > +\t\tstruct strbuf buf = STRBUF_INIT;\n> > +\t\tif (limit == 0) {\n> > +\t\t\tfprintf(stderr, \"    ...\\n\");\n> \n> How would you know, when you asked 20 and you showed 20 here, that there\n> is no more to come?\n\nIf there's more it will print the \"...\", if there isn't then it won't.\n\n> > +\t\t\tbreak;\n> > +\t\t}\n> \n> > +\t\tif (!commit->buffer) {\n> > +\t\t\tenum object_type type;\n> > +\t\t\tunsigned long size;\n> > +\t\t\tcommit->buffer =\n> > +\t\t\t\tread_sha1_file(commit->object.sha1, &type, &size);\n> > +\t\t\tif (!commit->buffer)\n> > +\t\t\t\tdie(\"Cannot read commit %s\", sha1_to_hex(commit->object.sha1));\n> > +\t\t}\n> > +\t\tformat_commit_message(commit, \"    %m %h %s\\n\", &buf, 0);\n> \n> Hmm, why so many spaces before %m and after %m?\n\nSo the summary lines are nicely indented with respect to the other output.\n\n> > -static int do_push(const char *repo, int flags)\n> > +static int do_push(const char *repo, int flags, int summary)\n> \n> Couldn't this be just another bit in the flag?  I didn't check but I\n> suspect you wouldn't have to touch the intermediate functions in the call\n> chain that way.\n\nIt can't just be a bit because the \"summary\" parameter contains number of\nsummary lines to print.\n\n> > +test_expect_success 'fetch --summary forced update' '\n> > +\tmk_empty &&\n> > +\t(\n> > ...\n> > +\t)\n> > +\n> > +'\n> \n> There are at least two missing combinations. (1) \"fetch --summary\" to\n> fetch a new branch, and (2) \"fetch --summary\" does not try segfaulting by\n> accessing unavailable information after a failed fetch.\n> \n> The same comment applies to the push side of the tests.\n\nWhat would be a good way to induce a failed fetch for this test?\n\n\n     --larry\n"},{"id":"133055","messageId":"20100130011056.GA20463@cthulhu","threadId":"20011","inReplyTo":"7viqhzm454.fsf@alter.siamese.dyndns.org","subject":"[PATCH v5] add --summary option to git-push and git-fetch","fromName":"Larry D'Anna","fromEmail":"larry@elder-gods.org","sentAt":"2010-01-30T01:10:56Z","receivedAt":"2010-01-30T01:10:56Z","isPatch":true,"sender":{"key":"larry@elder-gods.org","avatar":"https://avatars.githubusercontent.com/u/3013304?v=4"},"body":"--summary will cause git-push to output a one-line of each commit pushed.\n--summary=n will display at most n commits for each ref pushed.\n\n$ git push --dry-run --summary origin :\nTo /home/larry/gitsandbox/a\n   80f0e50..5593a38  master -> master\n    > 5593a38 foo\n    > 81c03f8 bar\n\nFetch works the same way.\n\nSigned-off-by: Larry D'Anna <larry@elder-gods.org>\n---\n Changes since last version: \n\n  * added more tests\n  * use clear_commit_marks instead of sledgehammer\n  * moved declaration of print_summary_for_push_or_fetch to the top of the header\n  * use egrep instead of grep -E\n\n Documentation/fetch-options.txt |    6 ++\n Documentation/git-push.txt      |    6 ++\n builtin-fetch.c                 |   24 ++++++--\n builtin-log.c                   |   35 +++++++++++\n builtin-push.c                  |   17 ++++--\n builtin.h                       |    2 +\n revision.c                      |   35 ++++++++++--\n revision.h                      |    1 +\n t/t5516-fetch-push.sh           |  121 +++++++++++++++++++++++++++++++++++++++\n transport.c                     |   42 +++++++++-----\n transport.h                     |    3 +-\n 11 files changed, 260 insertions(+), 32 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex fe716b2..53bf049 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -25,6 +25,12 @@ endif::git-pull[]\n \tfetches is a descendant of `<lbranch>`.  This option\n \toverrides that check.\n \n+--summary::\n+\tPrint a one-line summary of each commit fetched.\n+\n+--summary=<n>::\n+\tLike --summary, but with a limit of <n> commits per ref.\n+\n -k::\n --keep::\n \tKeep downloaded pack.\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 73a921c..25e0dec 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -86,6 +86,12 @@ nor in any Push line of the corresponding remotes file---see below).\n --dry-run::\n \tDo everything except actually send the updates.\n \n+--summary::\n+\tPrint a one-line summary of each commit pushed.\n+\n+--summary=<n>::\n+\tLike --summary, but with a limit of <n> commits per ref.\n+\n --porcelain::\n \tProduce machine-readable output.  The output status line for each ref\n \twill be tab-separated and sent to stdout instead of stderr.  The full\ndiff --git a/builtin-fetch.c b/builtin-fetch.c\nindex 8654fa7..4f56162 100644\n--- a/builtin-fetch.c\n+++ b/builtin-fetch.c\n@@ -32,6 +32,7 @@ static const char *depth;\n static const char *upload_pack;\n static struct strbuf default_rla = STRBUF_INIT;\n static struct transport *transport;\n+static int summary;\n \n static struct option builtin_fetch_options[] = {\n \tOPT__VERBOSITY(&verbosity),\n@@ -58,6 +59,9 @@ static struct option builtin_fetch_options[] = {\n \t\t    \"allow updating of HEAD ref\"),\n \tOPT_STRING(0, \"depth\", &depth, \"DEPTH\",\n \t\t   \"deepen history of shallow clone\"),\n+\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] fetched commits\",\n+\t  PARSE_OPT_OPTARG, NULL, 20\n+\t},\n \tOPT_END()\n };\n \n@@ -210,7 +214,8 @@ static int s_update_ref(const char *action,\n \n static int update_local_ref(struct ref *ref,\n \t\t\t    const char *remote,\n-\t\t\t    char *display)\n+\t\t\t    char *display,\n+\t\t\t    char *quickref)\n {\n \tstruct commit *current = NULL, *updated;\n \tenum object_type type;\n@@ -273,11 +278,12 @@ static int update_local_ref(struct ref *ref,\n \t\tsprintf(display, \"%c %-*s %-*s -> %s%s\", r ? '!' : '*',\n \t\t\tSUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,\n \t\t\tr ? \"  (unable to update local ref)\" : \"\");\n+\t\tif (!r)\n+\t\t\tstrcpy(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));\n \t\treturn r;\n \t}\n \n \tif (in_merge_bases(current, &updated, 1)) {\n-\t\tchar quickref[83];\n \t\tint r;\n \t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n \t\tstrcat(quickref, \"..\");\n@@ -288,7 +294,6 @@ static int update_local_ref(struct ref *ref,\n \t\t\tpretty_ref, r ? \"  (unable to update local ref)\" : \"\");\n \t\treturn r;\n \t} else if (force || ref->force) {\n-\t\tchar quickref[84];\n \t\tint r;\n \t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n \t\tstrcat(quickref, \"...\");\n@@ -314,6 +319,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \tstruct commit *commit;\n \tint url_len, i, note_len, shown_url = 0, rc = 0;\n \tchar note[1024];\n+\tchar quickref[84];\n \tconst char *what, *kind;\n \tstruct ref *rm;\n \tchar *url, *filename = dry_run ? \"/dev/null\" : git_path(\"FETCH_HEAD\");\n@@ -389,12 +395,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\t\tfputc(url[i], fp);\n \t\tfputc('\\n', fp);\n \n-\t\tif (ref)\n-\t\t\trc |= update_local_ref(ref, what, note);\n-\t\telse\n+\t\tif (ref) {\n+\t\t\t*quickref = 0;\n+\t\t\trc |= update_local_ref(ref, what, note, quickref);\n+\t\t} else {\n+\t\t\tstrcpy(quickref, find_unique_abbrev(rm->old_sha1, DEFAULT_ABBREV));\n \t\t\tsprintf(note, \"* %-*s %-*s -> FETCH_HEAD\",\n \t\t\t\tSUMMARY_WIDTH, *kind ? kind : \"branch\",\n \t\t\t\t REFCOL_WIDTH, *what ? what : \"HEAD\");\n+\t\t}\n \t\tif (*note) {\n \t\t\tif (verbosity >= 0 && !shown_url) {\n \t\t\t\tfprintf(stderr, \"From %.*s\\n\",\n@@ -404,6 +413,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\tif (verbosity >= 0)\n \t\t\t\tfprintf(stderr, \" %s\\n\", note);\n \t\t}\n+\t\tif (summary && quickref[0])\n+\t\t\tprint_summary_for_push_or_fetch(quickref, summary);\n+\n \t}\n \tfree(url);\n \tfclose(fp);\ndiff --git a/builtin-log.c b/builtin-log.c\nindex 8d16832..55ac4e4 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -1350,3 +1350,38 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \tfree_patch_ids(&ids);\n \treturn 0;\n }\n+\n+\n+void print_summary_for_push_or_fetch(const char *quickref, int limit)\n+{\n+\tstruct rev_info rev;\n+\tint i, max;\n+\tstruct object *obj;\n+\tstruct commit *commit;\n+\n+\tinit_revisions(&rev, NULL);\n+\trev.prune = 0;\n+\tassert(!handle_revision_arg_clearing_flags(quickref, &rev, 0, ALL_REV_FLAGS, 1));\n+\tassert(!prepare_revision_walk(&rev));\n+\n+\twhile ((commit = get_revision(&rev)) != NULL) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tif (limit == 0) {\n+\t\t\tfprintf(stderr, \"    ...\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!commit->buffer) {\n+\t\t\tenum object_type type;\n+\t\t\tunsigned long size;\n+\t\t\tcommit->buffer =\n+\t\t\t\tread_sha1_file(commit->object.sha1, &type, &size);\n+\t\t\tif (!commit->buffer)\n+\t\t\t\tdie(\"Cannot read commit %s\", sha1_to_hex(commit->object.sha1));\n+\t\t}\n+\t\tformat_commit_message(commit, \"    %m %h %s\\n\", &buf, 0);\n+\t\tfputs(buf.buf, stderr);\n+\t\tstrbuf_release(&buf);\n+\t\tlimit--;\n+\t}\n+\tfputs(\"\\n\", stderr);\n+}\ndiff --git a/builtin-push.c b/builtin-push.c\nindex 5df6608..30f5a61 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -101,7 +101,7 @@ static void setup_default_push_refspecs(void)\n \t}\n }\n \n-static int push_with_options(struct transport *transport, int flags)\n+static int push_with_options(struct transport *transport, int flags, int summary)\n {\n \tint err;\n \tint nonfastforward;\n@@ -114,7 +114,7 @@ static int push_with_options(struct transport *transport, int flags)\n \tif (flags & TRANSPORT_PUSH_VERBOSE)\n \t\tfprintf(stderr, \"Pushing to %s\\n\", transport->url);\n \terr = transport_push(transport, refspec_nr, refspec, flags,\n-\t\t\t     &nonfastforward);\n+\t\t\t\t\t\t summary, &nonfastforward);\n \tif (err != 0)\n \t\terror(\"failed to push some refs to '%s'\", transport->url);\n \n@@ -132,7 +132,7 @@ static int push_with_options(struct transport *transport, int flags)\n \treturn 1;\n }\n \n-static int do_push(const char *repo, int flags)\n+static int do_push(const char *repo, int flags, int summary)\n {\n \tint i, errs;\n \tstruct remote *remote = remote_get(repo);\n@@ -184,14 +184,14 @@ static int do_push(const char *repo, int flags)\n \t\tfor (i = 0; i < url_nr; i++) {\n \t\t\tstruct transport *transport =\n \t\t\t\ttransport_get(remote, url[i]);\n-\t\t\tif (push_with_options(transport, flags))\n+\t\t\tif (push_with_options(transport, flags, summary))\n \t\t\t\terrs++;\n \t\t}\n \t} else {\n \t\tstruct transport *transport =\n \t\t\ttransport_get(remote, NULL);\n \n-\t\tif (push_with_options(transport, flags))\n+ \t\tif (push_with_options(transport, flags, summary))\n \t\t\terrs++;\n \t}\n \treturn !!errs;\n@@ -203,6 +203,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \tint tags = 0;\n \tint rc;\n \tconst char *repo = NULL;\t/* default repository */\n+\tint summary = 0;\n+\n \tstruct option options[] = {\n \t\tOPT_BIT('q', \"quiet\", &flags, \"be quiet\", TRANSPORT_PUSH_QUIET),\n \t\tOPT_BIT('v', \"verbose\", &flags, \"be verbose\", TRANSPORT_PUSH_VERBOSE),\n@@ -218,6 +220,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOLEAN( 0 , \"thin\", &thin, \"use thin pack\"),\n \t\tOPT_STRING( 0 , \"receive-pack\", &receivepack, \"receive-pack\", \"receive pack program\"),\n \t\tOPT_STRING( 0 , \"exec\", &receivepack, \"receive-pack\", \"receive pack program\"),\n+\t\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] pushed commits\",\n+\t\t  PARSE_OPT_OPTARG, NULL, 20\n+\t\t},\n \t\tOPT_BIT('u', \"set-upstream\", &flags, \"set upstream for git pull/status\",\n \t\t\tTRANSPORT_PUSH_SET_UPSTREAM),\n \t\tOPT_END()\n@@ -239,7 +244,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tset_refspecs(argv + 1, argc - 1);\n \t}\n \n-\trc = do_push(repo, flags);\n+\trc = do_push(repo, flags, summary);\n \tif (rc == -1)\n \t\tusage_with_options(push_usage, options);\n \telse\ndiff --git a/builtin.h b/builtin.h\nindex e8202f3..ec411be 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -19,6 +19,7 @@ extern int commit_tree(const char *msg, unsigned char *tree,\n \t\tstruct commit_list *parents, unsigned char *ret,\n \t\tconst char *author);\n extern int check_pager_config(const char *cmd);\n+extern void print_summary_for_push_or_fetch(const char *quickref, int limit);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n extern int cmd_annotate(int argc, const char **argv, const char *prefix);\n@@ -122,4 +123,5 @@ extern int cmd_show_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);\n extern int cmd_replace(int argc, const char **argv, const char *prefix);\n \n+\n #endif\ndiff --git a/revision.c b/revision.c\nindex 3ba6d99..cf3103a 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -768,7 +768,7 @@ static void handle_reflog(struct rev_info *revs, unsigned flags)\n \tfor_each_reflog(handle_one_reflog, &cb);\n }\n \n-static int add_parents_only(struct rev_info *revs, const char *arg, int flags)\n+static int add_parents_only(struct rev_info *revs, const char *arg, int flags, int flags_to_clear)\n {\n \tunsigned char sha1[20];\n \tstruct object *it;\n@@ -792,6 +792,8 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags)\n \tif (it->type != OBJ_COMMIT)\n \t\treturn 0;\n \tcommit = (struct commit *)it;\n+\tif (flags_to_clear)\n+\t    clear_commit_marks(commit, flags_to_clear); \n \tfor (parents = commit->parents; parents; parents = parents->next) {\n \t\tit = &parents->item->object;\n \t\tit->flags |= flags;\n@@ -887,9 +889,18 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,\n \t\t\tint flags,\n \t\t\tint cant_be_filename)\n {\n+    return handle_revision_arg_clearing_flags (arg, revs, flags, 0, cant_be_filename); \n+}\n+\n+\n+\n+int handle_revision_arg_clearing_flags (const char *arg, struct rev_info *revs,\n+\t\t\t\t\tint flags,  int flags_to_clear, \n+\t\t\t\t\tint cant_be_filename)\n+{\n \tunsigned mode;\n \tchar *dotdot;\n-\tstruct object *object;\n+\tstruct object *object, *object_deref;\n \tunsigned char sha1[20];\n \tint local_flags;\n \n@@ -915,6 +926,10 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,\n \n \t\t\ta = lookup_commit_reference(from_sha1);\n \t\t\tb = lookup_commit_reference(sha1);\n+\t\t\tif (a && flags_to_clear) \n+\t\t\t    clear_commit_marks(a, flags_to_clear); \n+\t\t\tif (b && flags_to_clear)\n+\t\t\t    clear_commit_marks(b, flags_to_clear); \n \t\t\tif (!a || !b) {\n \t\t\t\tdie(symmetric ?\n \t\t\t\t    \"Invalid symmetric difference expression %s...%s\" :\n@@ -945,14 +960,14 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,\n \tdotdot = strstr(arg, \"^@\");\n \tif (dotdot && !dotdot[2]) {\n \t\t*dotdot = 0;\n-\t\tif (add_parents_only(revs, arg, flags))\n+\t\tif (add_parents_only(revs, arg, flags, flags_to_clear))\n \t\t\treturn 0;\n \t\t*dotdot = '^';\n \t}\n \tdotdot = strstr(arg, \"^!\");\n \tif (dotdot && !dotdot[2]) {\n \t\t*dotdot = 0;\n-\t\tif (!add_parents_only(revs, arg, flags ^ UNINTERESTING))\n+\t\tif (!add_parents_only(revs, arg, flags ^ UNINTERESTING, flags_to_clear))\n \t\t\t*dotdot = '^';\n \t}\n \n@@ -965,7 +980,17 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,\n \t\treturn -1;\n \tif (!cant_be_filename)\n \t\tverify_non_filename(revs->prefix, arg);\n-\tobject = get_reference(revs, arg, sha1, flags ^ local_flags);\n+\n+\tobject = parse_object(sha1); \n+\tif (!object)\n+\t    die(\"bad object %s\", arg);\n+\t\n+\tobject_deref = deref_tag(object, NULL, 0); \n+\tif (object_deref && object_deref->type == OBJ_COMMIT)\n+\t    if (flags_to_clear)\n+\t\tclear_commit_marks((struct commit *) object_deref, flags_to_clear); \n+\n+\tobject->flags |= flags ^ local_flags; \n \tadd_pending_object_with_mode(revs, object, arg, mode);\n \treturn 0;\n }\ndiff --git a/revision.h b/revision.h\nindex a14deef..36f78bb 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -143,6 +143,7 @@ extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ct\n \t\t\t\t const struct option *options,\n \t\t\t\t const char * const usagestr[]);\n extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);\n+extern int handle_revision_arg_clearing_flags(const char *arg, struct rev_info *revs,int flags, int flags_to_clear, int cant_be_filename);\n \n extern int prepare_revision_walk(struct rev_info *revs);\n extern struct commit *get_revision(struct rev_info *revs);\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 0f04b2e..3472aaa 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -91,6 +91,71 @@ test_expect_success 'fetch without wildcard' '\n \t)\n '\n \n+\n+test_expect_success 'fetch --summary branch update' '\n+\tmk_empty &&\n+\t(\n+\t\tcd testrepo &&\n+\n+\t\tgit fetch  .. refs/heads/master:refs/remotes/origin/master &&\n+\n+\t\tgit update-ref refs/remotes/origin/master refs/remotes/origin/master^ &&\n+\n+\t\tgit fetch --summary .. refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l) &&\n+\n+\t\t  egrep  \"^    > [a-fA-F0-9]+ second$\" stderr &&\n+\t\t! egrep  \"^    > [a-fA-F0-9]+ repo$\" stderr\n+\t)\n+\t'\n+\n+test_expect_success 'fetch --summary new branch' '\n+\tmk_empty &&p\n+\t(\n+\t\tcd testrepo &&\n+\t\tgit fetch --summary .. refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l) &&\n+\n+\t\tegrep  \"^    > [a-fA-F0-9]+ second$\" stderr &&\n+\t\tegrep  \"^    > [a-fA-F0-9]+ repo$\" stderr\n+\t)\n+        '\n+\n+test_expect_success 'fetch --summary forced update' '\n+\tmk_empty &&\n+\t(\n+\t\tcd testrepo &&\n+\t\tgit fetch .. refs/heads/master:refs/remotes/origin/master &&\n+\n+\t\tgit checkout refs/remotes/origin/master^ &&\n+\t\t: >path3 &&\n+\t\tgit add path3 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m third &&\n+\t\tgit update-ref refs/remotes/origin/master HEAD &&\n+\n+\t\tgit fetch .. -f --summary refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l) &&\n+\n+\t\tegrep  \"^    < [a-fA-F0-9]+ third$\" stderr &&\n+\t\tegrep  \"^    > [a-fA-F0-9]+ second$\" stderr\n+\t)\n+\n+'\n+\n test_expect_success 'fetch with wildcard' '\n \tmk_empty &&\n \t(\n@@ -153,6 +218,62 @@ test_expect_success 'push without wildcard' '\n \t)\n '\n \n+test_expect_success 'push --summary new branch' '\n+\tmk_empty &&\n+\n+\tgit push --summary testrepo refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\t(\n+\t\tcd testrepo &&\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l)\n+\t) &&\n+\n+\tegrep  \"^    > [a-fA-F0-9]+ second$\" stderr &&\n+\tegrep  \"^    > [a-fA-F0-9]+ repo$\" stderr\n+'\n+\n+test_expect_success 'push --summary branch update' '\n+\tmk_empty &&\n+\n+\tgit push testrepo refs/heads/master:refs/remotes/origin/master &&\n+\n+\tgit --git-dir testrepo/.git update-ref refs/remotes/origin/master refs/remotes/origin/master^ &&\n+\n+\tgit push --summary testrepo refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\t(\n+\t\tcd testrepo &&\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l)\n+\t) &&\n+\n+\t  egrep  \"^    > [a-fA-F0-9]+ second$\" stderr &&\n+\t! egrep  \"^    > [a-fA-F0-9]+ repo$\" stderr\n+'\n+\n+\n+test_expect_success 'push --summary forced update' '\n+\tmk_empty &&\n+\n+\tgit push testrepo refs/heads/master:refs/remotes/origin/master &&\n+\n+\tgit checkout master^ &&\n+\t: >path3 &&\n+\tgit add path3 &&\n+\ttest_tick &&\n+\tgit commit -a -m third &&\n+\n+\tgit push --summary -f testrepo HEAD:refs/remotes/origin/master 2>stderr &&\n+\n+\tegrep  \"^    < [a-fA-F0-9]+ second$\" stderr &&\n+\tegrep  \"^    > [a-fA-F0-9]+ third$\" stderr &&\n+\n+\tgit checkout master\n+'\n+\n test_expect_success 'push with wildcard' '\n \tmk_empty &&\n \ndiff --git a/transport.c b/transport.c\nindex 3846aac..1e3fa7a 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -8,6 +8,7 @@\n #include \"bundle.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"builtin.h\"\n #include \"branch.h\"\n \n /* rsync support */\n@@ -642,17 +643,20 @@ static const char *status_abbrev(unsigned char sha1[20])\n \treturn find_unique_abbrev(sha1, DEFAULT_ABBREV);\n }\n \n-static void print_ok_ref_status(struct ref *ref, int porcelain)\n+static void print_ok_ref_status(struct ref *ref, int porcelain, int summary)\n {\n+\tchar quickref[84];\n+\tint summary_impossible = 0;\n+\n \tif (ref->deletion)\n \t\tprint_ref_status('-', \"[deleted]\", ref, NULL, NULL, porcelain);\n-\telse if (is_null_sha1(ref->old_sha1))\n+\telse if (is_null_sha1(ref->old_sha1)) {\n \t\tprint_ref_status('*',\n \t\t\t(!prefixcmp(ref->name, \"refs/tags/\") ? \"[new tag]\" :\n \t\t\t\"[new branch]\"),\n \t\t\tref, ref->peer_ref, NULL, porcelain);\n-\telse {\n-\t\tchar quickref[84];\n+\t\tstrcpy(quickref, status_abbrev(ref->new_sha1));\n+\t} else {\n \t\tchar type;\n \t\tconst char *msg;\n \n@@ -661,6 +665,8 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \t\t\tstrcat(quickref, \"...\");\n \t\t\ttype = '+';\n \t\t\tmsg = \"forced update\";\n+\t\t\tif (!lookup_commit_reference_gently(ref->old_sha1, 1))\n+\t\t\t\tsummary_impossible = 1;\n \t\t} else {\n \t\t\tstrcat(quickref, \"..\");\n \t\t\ttype = ' ';\n@@ -670,9 +676,17 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \n \t\tprint_ref_status(type, quickref, ref, ref->peer_ref, msg, porcelain);\n \t}\n+\n+\tif (summary) {\n+\t\tif (summary_impossible) {\n+\t\t\tfprintf(stderr, \"    %s is unavailable\\n\", status_abbrev(ref->old_sha1));\n+\t\t} else {\n+\t\t\tprint_summary_for_push_or_fetch(quickref, summary);\n+\t\t}\n+\t}\n }\n \n-static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)\n+static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain, int summary)\n {\n \tif (!count)\n \t\tfprintf(stderr, \"To %s\\n\", dest);\n@@ -704,7 +718,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n \t\t\t\t\t\t \"remote failed to report status\", porcelain);\n \t\tbreak;\n \tcase REF_STATUS_OK:\n-\t\tprint_ok_ref_status(ref, porcelain);\n+\t\tprint_ok_ref_status(ref, porcelain, summary);\n \t\tbreak;\n \t}\n \n@@ -712,7 +726,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n }\n \n static void print_push_status(const char *dest, struct ref *refs,\n-\t\t\t      int verbose, int porcelain, int * nonfastforward)\n+\t\t\t\t\t\t\t  int verbose, int porcelain, int summary, int *nonfastforward)\n {\n \tstruct ref *ref;\n \tint n = 0;\n@@ -720,19 +734,19 @@ static void print_push_status(const char *dest, struct ref *refs,\n \tif (verbose) {\n \t\tfor (ref = refs; ref; ref = ref->next)\n \t\t\tif (ref->status == REF_STATUS_UPTODATE)\n-\t\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \t}\n \n \tfor (ref = refs; ref; ref = ref->next)\n \t\tif (ref->status == REF_STATUS_OK)\n-\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \n \t*nonfastforward = 0;\n \tfor (ref = refs; ref; ref = ref->next) {\n \t\tif (ref->status != REF_STATUS_NONE &&\n \t\t    ref->status != REF_STATUS_UPTODATE &&\n \t\t    ref->status != REF_STATUS_OK)\n-\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \t\tif (ref->status == REF_STATUS_REJECT_NONFASTFORWARD)\n \t\t\t*nonfastforward = 1;\n \t}\n@@ -1014,8 +1028,8 @@ int transport_set_option(struct transport *transport,\n }\n \n int transport_push(struct transport *transport,\n-\t\t   int refspec_nr, const char **refspec, int flags,\n-\t\t   int *nonfastforward)\n+\t\t\t\t   int refspec_nr, const char **refspec,\n+\t\t\t\t   int flags, int summary, int *nonfastforward)\n {\n \t*nonfastforward = 0;\n \tverify_remote_names(refspec_nr, refspec);\n@@ -1058,8 +1072,8 @@ int transport_push(struct transport *transport,\n \n \t\tif (!quiet || err)\n \t\t\tprint_push_status(transport->url, remote_refs,\n-\t\t\t\t\tverbose | porcelain, porcelain,\n-\t\t\t\t\tnonfastforward);\n+\t\t\t\t\t\t\t  verbose | porcelain, porcelain,\n+\t\t\t\t\t\t\t  summary, nonfastforward);\n \n \t\tif (flags & TRANSPORT_PUSH_SET_UPSTREAM)\n \t\t\tset_upstreams(transport, remote_refs, pretend);\ndiff --git a/transport.h b/transport.h\nindex 7cea5cc..f87b2e9 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -124,9 +124,10 @@ int transport_set_option(struct transport *transport, const char *name,\n \t\t\t const char *value);\n \n int transport_push(struct transport *connection,\n-\t\t   int refspec_nr, const char **refspec, int flags,\n+\t\t   int refspec_nr, const char **refspec, int flags, int summary,\n \t\t   int * nonfastforward);\n \n+\n const struct ref *transport_get_remote_refs(struct transport *transport);\n \n int transport_fetch_refs(struct transport *transport, struct ref *refs);\n-- \n1.6.3.3.415.gbe1e\n"},{"id":"133057","messageId":"7vzl3w9yst.fsf@alter.siamese.dyndns.org","threadId":"20011","inReplyTo":"20100130005948.GA14938@cthulhu","subject":"Re: [PATCH v4] add --summary option to git-push and git-fetch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-30T01:17:06Z","receivedAt":"2010-01-30T01:17:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Larry D'Anna <larry@elder-gods.org> writes:\n\nPlease don't use M-F-T to deflect a direct response meant to you away from\nyou.  I also do not want people wasting _my_ time by following your M-F-T\nand sending their comments meant to _you_ coming to me with my address on\nTo: line, as I prioritize incoming messages based on where my address is\nin the header, and do not want you waste _other's_ time by making them\ncorrect their \"To:\" like to avoid wasting my time.\n\n>> > @@ -373,12 +379,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n>> >  \t\t\t\tfputc(url[i], fp);\n>> >  \t\tfputc('\\n', fp);\n>> >  \n>> > -\t\tif (ref)\n>> > -\t\t\trc |= update_local_ref(ref, what, note);\n>> > -\t\telse\n>> > +\t\tif (ref) {\n>> > +\t\t\t*quickref = 0;\n>> > +\t\t\trc |= update_local_ref(ref, what, note, quickref);\n>> \n>> Makes me wonder why update_local_ref() does not put that NUL upon entry.\n>\n> I'm not sure what you mean.  Could you elaborate?\n\nWhy is it necessary for the caller to do \"*quickref = '\\0'\" before calling\nupdate_local_ref()?  Shouldn't your updated u-l-r be doing that clearing,\nso that the callers don't have to worry about it?\n\n>> > +\tinit_revisions(&rev, NULL);\n>> > +\trev.prune = 0;\n>> > +\tassert(!handle_revision_arg(quickref, &rev, 0, 1));\n>> > +\tassert(!prepare_revision_walk(&rev));\n>> > +\n>> > +\twhile ((commit = get_revision(&rev)) != NULL) {\n>> > +\t\tstruct strbuf buf = STRBUF_INIT;\n>> > +\t\tif (limit == 0) {\n>> > +\t\t\tfprintf(stderr, \"    ...\\n\");\n>> \n>> How would you know, when you asked 20 and you showed 20 here, that there\n>> is no more to come?\n>\n> If there's more it will print the \"...\", if there isn't then it won't.\n\nIf your limit is 20 and if you unconditionally say \"...\" after pulling 20\nfrom the pool, the consumer of your output would think \"Ah, I see 20 but\nthat is only I asked for 20, and the ... means there are more\".  But that\nis incorrect because your 21st call to get_revision() might have yielded\nNULL in which case you had only 20 after all.\n\nYou cannot do \"...\" correctly without pulling one more than the limit from\nthe pool.\n\n>> > +test_expect_success 'fetch --summary forced update' '\n>> > +\tmk_empty &&\n>> > +\t(\n>> > ...\n>> > +\t)\n>> > +\n>> > +'\n>> \n>> There are at least two missing combinations. (1) \"fetch --summary\" to\n>> fetch a new branch, and (2) \"fetch --summary\" does not try segfaulting by\n>> accessing unavailable information after a failed fetch.\n>> \n>> The same comment applies to the push side of the tests.\n>\n> What would be a good way to induce a failed fetch for this test?\n\nNot having a valid ref or repo, perhaps.  I dunno---it's been quite a\nwhile since I saw the patch.\n"},{"id":"133056","messageId":"7vljfg9yoa.fsf@alter.siamese.dyndns.org","threadId":"20011","inReplyTo":"20100130005948.GA14938@cthulhu","subject":"Re: [PATCH v4] add --summary option to git-push and git-fetch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-30T01:19:49Z","receivedAt":"2010-01-30T01:19:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Larry D'Anna <larry@elder-gods.org> writes:\n\nPlease don't use M-F-T to deflect a direct response meant to you away from\nyou.  I also do not want people wasting _my_ time by following your M-F-T\nand sending their comments meant to _you_ coming to me with my address on\nTo: line, as I prioritize incoming messages based on where my address is\nin the header, and do not want you waste _other's_ time by making them\ncorrect their \"To:\" like to avoid wasting my time.\n\n>> > @@ -373,12 +379,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n>> >  \t\t\t\tfputc(url[i], fp);\n>> >  \t\tfputc('\\n', fp);\n>> >  \n>> > -\t\tif (ref)\n>> > -\t\t\trc |= update_local_ref(ref, what, note);\n>> > -\t\telse\n>> > +\t\tif (ref) {\n>> > +\t\t\t*quickref = 0;\n>> > +\t\t\trc |= update_local_ref(ref, what, note, quickref);\n>> \n>> Makes me wonder why update_local_ref() does not put that NUL upon entry.\n>\n> I'm not sure what you mean.  Could you elaborate?\n\nWhy is it necessary for the caller to do \"*quickref = '\\0'\" before calling\nupdate_local_ref()?  Shouldn't your updated u-l-r be doing that clearing,\nso that the callers don't have to worry about it?\n\n>> > +\tinit_revisions(&rev, NULL);\n>> > +\trev.prune = 0;\n>> > +\tassert(!handle_revision_arg(quickref, &rev, 0, 1));\n>> > +\tassert(!prepare_revision_walk(&rev));\n>> > +\n>> > +\twhile ((commit = get_revision(&rev)) != NULL) {\n>> > +\t\tstruct strbuf buf = STRBUF_INIT;\n>> > +\t\tif (limit == 0) {\n>> > +\t\t\tfprintf(stderr, \"    ...\\n\");\n>> \n>> How would you know, when you asked 20 and you showed 20 here, that there\n>> is no more to come?\n>\n> If there's more it will print the \"...\", if there isn't then it won't.\n\nIf your limit is 20 and if you unconditionally say \"...\" after pulling 20\nfrom the pool, the consumer of your output would think \"Ah, I see 20 but\nthat is only I asked for 20, and the ... means there are more\".  But that\nis incorrect because your 21st call to get_revision() might have yielded\nNULL in which case you had only 20 after all.\n\nYou cannot do \"...\" correctly without pulling one more than the limit from\nthe pool.\n\n>> > +test_expect_success 'fetch --summary forced update' '\n>> > +\tmk_empty &&\n>> > +\t(\n>> > ...\n>> > +\t)\n>> > +\n>> > +'\n>> \n>> There are at least two missing combinations. (1) \"fetch --summary\" to\n>> fetch a new branch, and (2) \"fetch --summary\" does not try segfaulting by\n>> accessing unavailable information after a failed fetch.\n>> \n>> The same comment applies to the push side of the tests.\n>\n> What would be a good way to induce a failed fetch for this test?\n\nNot having a valid ref or repo, perhaps.  I dunno---it's been quite a\nwhile since I saw the patch.\n"},{"id":"133059","messageId":"7vwrz08juy.fsf@alter.siamese.dyndns.org","threadId":"20011","inReplyTo":"7vzl3w9yst.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v4] add --summary option to git-push and git-fetch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-30T01:25:09Z","receivedAt":"2010-01-30T01:25:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>>> How would you know, when you asked 20 and you showed 20 here, that there\n>>> is no more to come?\n>>\n>> If there's more it will print the \"...\", if there isn't then it won't.\n>\n> If your limit is 20 and if you unconditionally say \"...\" after pulling 20\n> from the pool, the consumer of your output would think \"Ah, I see 20 but\n> that is only I asked for 20, and the ... means there are more\".  But that\n> is incorrect because your 21st call to get_revision() might have yielded\n> NULL in which case you had only 20 after all.\n>\n> You cannot do \"...\" correctly without pulling one more than the limit from\n> the pool.\n\nAh, I either misremembered the loop or didn't read it correctly.\n\nEither way, the loop in your v5 looks correct (\"read, check count to say\n... and exit if limit goes down to zero, show one, decrement and go to\ntop\").\n"},{"id":"133062","messageId":"20100130020548.GA29343@cthulhu","threadId":"20011","inReplyTo":"20100130011056.GA20463@cthulhu","subject":"[PATCH v6] add --summary option to git-push and git-fetch","fromName":"Larry D'Anna","fromEmail":"larry@elder-gods.org","sentAt":"2010-01-30T02:05:48Z","receivedAt":"2010-01-30T02:05:48Z","isPatch":true,"sender":{"key":"larry@elder-gods.org","avatar":"https://avatars.githubusercontent.com/u/3013304?v=4"},"body":"--summary will cause git-push to output a one-line of each commit pushed.\n--summary=n will display at most n commits for each ref pushed.\n\n$ git push --dry-run --summary origin :\nTo /home/larry/gitsandbox/a\n   80f0e50..5593a38  master -> master\n    > 5593a38 foo\n    > 81c03f8 bar\n\nFetch works the same way.\n\nSigned-off-by: Larry D'Anna <larry@elder-gods.org>\n---\n Changes since last version:\n \n * moved *quickref = 0 into update_local_ref\n * added tests to confirm failed pushes and fetches don't segfault\n\n Documentation/fetch-options.txt |    6 ++\n Documentation/git-push.txt      |    6 ++\n builtin-fetch.c                 |   22 +++++--\n builtin-log.c                   |   35 ++++++++++\n builtin-push.c                  |   17 +++--\n builtin.h                       |    2 +\n revision.c                      |   35 +++++++++--\n revision.h                      |    1 +\n t/t5516-fetch-push.sh           |  136 +++++++++++++++++++++++++++++++++++++++\n transport.c                     |   42 ++++++++----\n transport.h                     |    3 +-\n 11 files changed, 274 insertions(+), 31 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex fe716b2..53bf049 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -25,6 +25,12 @@ endif::git-pull[]\n \tfetches is a descendant of `<lbranch>`.  This option\n \toverrides that check.\n \n+--summary::\n+\tPrint a one-line summary of each commit fetched.\n+\n+--summary=<n>::\n+\tLike --summary, but with a limit of <n> commits per ref.\n+\n -k::\n --keep::\n \tKeep downloaded pack.\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 73a921c..25e0dec 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -86,6 +86,12 @@ nor in any Push line of the corresponding remotes file---see below).\n --dry-run::\n \tDo everything except actually send the updates.\n \n+--summary::\n+\tPrint a one-line summary of each commit pushed.\n+\n+--summary=<n>::\n+\tLike --summary, but with a limit of <n> commits per ref.\n+\n --porcelain::\n \tProduce machine-readable output.  The output status line for each ref\n \twill be tab-separated and sent to stdout instead of stderr.  The full\ndiff --git a/builtin-fetch.c b/builtin-fetch.c\nindex 8654fa7..5e1e91b 100644\n--- a/builtin-fetch.c\n+++ b/builtin-fetch.c\n@@ -32,6 +32,7 @@ static const char *depth;\n static const char *upload_pack;\n static struct strbuf default_rla = STRBUF_INIT;\n static struct transport *transport;\n+static int summary;\n \n static struct option builtin_fetch_options[] = {\n \tOPT__VERBOSITY(&verbosity),\n@@ -58,6 +59,9 @@ static struct option builtin_fetch_options[] = {\n \t\t    \"allow updating of HEAD ref\"),\n \tOPT_STRING(0, \"depth\", &depth, \"DEPTH\",\n \t\t   \"deepen history of shallow clone\"),\n+\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] fetched commits\",\n+\t  PARSE_OPT_OPTARG, NULL, 20\n+\t},\n \tOPT_END()\n };\n \n@@ -210,13 +214,15 @@ static int s_update_ref(const char *action,\n \n static int update_local_ref(struct ref *ref,\n \t\t\t    const char *remote,\n-\t\t\t    char *display)\n+\t\t\t    char *display,\n+\t\t\t    char *quickref)\n {\n \tstruct commit *current = NULL, *updated;\n \tenum object_type type;\n \tstruct branch *current_branch = branch_get(NULL);\n \tconst char *pretty_ref = prettify_refname(ref->name);\n \n+\t*quickref = 0;\n \t*display = 0;\n \ttype = sha1_object_info(ref->new_sha1, NULL);\n \tif (type < 0)\n@@ -273,11 +279,12 @@ static int update_local_ref(struct ref *ref,\n \t\tsprintf(display, \"%c %-*s %-*s -> %s%s\", r ? '!' : '*',\n \t\t\tSUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,\n \t\t\tr ? \"  (unable to update local ref)\" : \"\");\n+\t\tif (!r)\n+\t\t\tstrcpy(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));\n \t\treturn r;\n \t}\n \n \tif (in_merge_bases(current, &updated, 1)) {\n-\t\tchar quickref[83];\n \t\tint r;\n \t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n \t\tstrcat(quickref, \"..\");\n@@ -288,7 +295,6 @@ static int update_local_ref(struct ref *ref,\n \t\t\tpretty_ref, r ? \"  (unable to update local ref)\" : \"\");\n \t\treturn r;\n \t} else if (force || ref->force) {\n-\t\tchar quickref[84];\n \t\tint r;\n \t\tstrcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));\n \t\tstrcat(quickref, \"...\");\n@@ -314,6 +320,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \tstruct commit *commit;\n \tint url_len, i, note_len, shown_url = 0, rc = 0;\n \tchar note[1024];\n+\tchar quickref[84];\n \tconst char *what, *kind;\n \tstruct ref *rm;\n \tchar *url, *filename = dry_run ? \"/dev/null\" : git_path(\"FETCH_HEAD\");\n@@ -390,11 +397,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\tfputc('\\n', fp);\n \n \t\tif (ref)\n-\t\t\trc |= update_local_ref(ref, what, note);\n-\t\telse\n+\t\t\trc |= update_local_ref(ref, what, note, quickref);\n+\t\telse {\n+\t\t\tstrcpy(quickref, find_unique_abbrev(rm->old_sha1, DEFAULT_ABBREV));\n \t\t\tsprintf(note, \"* %-*s %-*s -> FETCH_HEAD\",\n \t\t\t\tSUMMARY_WIDTH, *kind ? kind : \"branch\",\n \t\t\t\t REFCOL_WIDTH, *what ? what : \"HEAD\");\n+\t\t}\n \t\tif (*note) {\n \t\t\tif (verbosity >= 0 && !shown_url) {\n \t\t\t\tfprintf(stderr, \"From %.*s\\n\",\n@@ -404,6 +413,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\tif (verbosity >= 0)\n \t\t\t\tfprintf(stderr, \" %s\\n\", note);\n \t\t}\n+\t\tif (summary && quickref[0])\n+\t\t\tprint_summary_for_push_or_fetch(quickref, summary);\n+\n \t}\n \tfree(url);\n \tfclose(fp);\ndiff --git a/builtin-log.c b/builtin-log.c\nindex 8d16832..55ac4e4 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -1350,3 +1350,38 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)\n \tfree_patch_ids(&ids);\n \treturn 0;\n }\n+\n+\n+void print_summary_for_push_or_fetch(const char *quickref, int limit)\n+{\n+\tstruct rev_info rev;\n+\tint i, max;\n+\tstruct object *obj;\n+\tstruct commit *commit;\n+\n+\tinit_revisions(&rev, NULL);\n+\trev.prune = 0;\n+\tassert(!handle_revision_arg_clearing_flags(quickref, &rev, 0, ALL_REV_FLAGS, 1));\n+\tassert(!prepare_revision_walk(&rev));\n+\n+\twhile ((commit = get_revision(&rev)) != NULL) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tif (limit == 0) {\n+\t\t\tfprintf(stderr, \"    ...\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!commit->buffer) {\n+\t\t\tenum object_type type;\n+\t\t\tunsigned long size;\n+\t\t\tcommit->buffer =\n+\t\t\t\tread_sha1_file(commit->object.sha1, &type, &size);\n+\t\t\tif (!commit->buffer)\n+\t\t\t\tdie(\"Cannot read commit %s\", sha1_to_hex(commit->object.sha1));\n+\t\t}\n+\t\tformat_commit_message(commit, \"    %m %h %s\\n\", &buf, 0);\n+\t\tfputs(buf.buf, stderr);\n+\t\tstrbuf_release(&buf);\n+\t\tlimit--;\n+\t}\n+\tfputs(\"\\n\", stderr);\n+}\ndiff --git a/builtin-push.c b/builtin-push.c\nindex 5df6608..30f5a61 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -101,7 +101,7 @@ static void setup_default_push_refspecs(void)\n \t}\n }\n \n-static int push_with_options(struct transport *transport, int flags)\n+static int push_with_options(struct transport *transport, int flags, int summary)\n {\n \tint err;\n \tint nonfastforward;\n@@ -114,7 +114,7 @@ static int push_with_options(struct transport *transport, int flags)\n \tif (flags & TRANSPORT_PUSH_VERBOSE)\n \t\tfprintf(stderr, \"Pushing to %s\\n\", transport->url);\n \terr = transport_push(transport, refspec_nr, refspec, flags,\n-\t\t\t     &nonfastforward);\n+\t\t\t\t\t\t summary, &nonfastforward);\n \tif (err != 0)\n \t\terror(\"failed to push some refs to '%s'\", transport->url);\n \n@@ -132,7 +132,7 @@ static int push_with_options(struct transport *transport, int flags)\n \treturn 1;\n }\n \n-static int do_push(const char *repo, int flags)\n+static int do_push(const char *repo, int flags, int summary)\n {\n \tint i, errs;\n \tstruct remote *remote = remote_get(repo);\n@@ -184,14 +184,14 @@ static int do_push(const char *repo, int flags)\n \t\tfor (i = 0; i < url_nr; i++) {\n \t\t\tstruct transport *transport =\n \t\t\t\ttransport_get(remote, url[i]);\n-\t\t\tif (push_with_options(transport, flags))\n+\t\t\tif (push_with_options(transport, flags, summary))\n \t\t\t\terrs++;\n \t\t}\n \t} else {\n \t\tstruct transport *transport =\n \t\t\ttransport_get(remote, NULL);\n \n-\t\tif (push_with_options(transport, flags))\n+ \t\tif (push_with_options(transport, flags, summary))\n \t\t\terrs++;\n \t}\n \treturn !!errs;\n@@ -203,6 +203,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \tint tags = 0;\n \tint rc;\n \tconst char *repo = NULL;\t/* default repository */\n+\tint summary = 0;\n+\n \tstruct option options[] = {\n \t\tOPT_BIT('q', \"quiet\", &flags, \"be quiet\", TRANSPORT_PUSH_QUIET),\n \t\tOPT_BIT('v', \"verbose\", &flags, \"be verbose\", TRANSPORT_PUSH_VERBOSE),\n@@ -218,6 +220,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOLEAN( 0 , \"thin\", &thin, \"use thin pack\"),\n \t\tOPT_STRING( 0 , \"receive-pack\", &receivepack, \"receive-pack\", \"receive pack program\"),\n \t\tOPT_STRING( 0 , \"exec\", &receivepack, \"receive-pack\", \"receive pack program\"),\n+\t\t{ OPTION_INTEGER, 0, \"summary\", &summary, \"n\", \"print a summary of [at most n] pushed commits\",\n+\t\t  PARSE_OPT_OPTARG, NULL, 20\n+\t\t},\n \t\tOPT_BIT('u', \"set-upstream\", &flags, \"set upstream for git pull/status\",\n \t\t\tTRANSPORT_PUSH_SET_UPSTREAM),\n \t\tOPT_END()\n@@ -239,7 +244,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tset_refspecs(argv + 1, argc - 1);\n \t}\n \n-\trc = do_push(repo, flags);\n+\trc = do_push(repo, flags, summary);\n \tif (rc == -1)\n \t\tusage_with_options(push_usage, options);\n \telse\ndiff --git a/builtin.h b/builtin.h\nindex e8202f3..ec411be 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -19,6 +19,7 @@ extern int commit_tree(const char *msg, unsigned char *tree,\n \t\tstruct commit_list *parents, unsigned char *ret,\n \t\tconst char *author);\n extern int check_pager_config(const char *cmd);\n+extern void print_summary_for_push_or_fetch(const char *quickref, int limit);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n extern int cmd_annotate(int argc, const char **argv, const char *prefix);\n@@ -122,4 +123,5 @@ extern int cmd_show_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);\n extern int cmd_replace(int argc, const char **argv, const char *prefix);\n \n+\n #endif\ndiff --git a/revision.c b/revision.c\nindex 3ba6d99..cf3103a 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -768,7 +768,7 @@ static void handle_reflog(struct rev_info *revs, unsigned flags)\n \tfor_each_reflog(handle_one_reflog, &cb);\n }\n \n-static int add_parents_only(struct rev_info *revs, const char *arg, int flags)\n+static int add_parents_only(struct rev_info *revs, const char *arg, int flags, int flags_to_clear)\n {\n \tunsigned char sha1[20];\n \tstruct object *it;\n@@ -792,6 +792,8 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags)\n \tif (it->type != OBJ_COMMIT)\n \t\treturn 0;\n \tcommit = (struct commit *)it;\n+\tif (flags_to_clear)\n+\t    clear_commit_marks(commit, flags_to_clear); \n \tfor (parents = commit->parents; parents; parents = parents->next) {\n \t\tit = &parents->item->object;\n \t\tit->flags |= flags;\n@@ -887,9 +889,18 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,\n \t\t\tint flags,\n \t\t\tint cant_be_filename)\n {\n+    return handle_revision_arg_clearing_flags (arg, revs, flags, 0, cant_be_filename); \n+}\n+\n+\n+\n+int handle_revision_arg_clearing_flags (const char *arg, struct rev_info *revs,\n+\t\t\t\t\tint flags,  int flags_to_clear, \n+\t\t\t\t\tint cant_be_filename)\n+{\n \tunsigned mode;\n \tchar *dotdot;\n-\tstruct object *object;\n+\tstruct object *object, *object_deref;\n \tunsigned char sha1[20];\n \tint local_flags;\n \n@@ -915,6 +926,10 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,\n \n \t\t\ta = lookup_commit_reference(from_sha1);\n \t\t\tb = lookup_commit_reference(sha1);\n+\t\t\tif (a && flags_to_clear) \n+\t\t\t    clear_commit_marks(a, flags_to_clear); \n+\t\t\tif (b && flags_to_clear)\n+\t\t\t    clear_commit_marks(b, flags_to_clear); \n \t\t\tif (!a || !b) {\n \t\t\t\tdie(symmetric ?\n \t\t\t\t    \"Invalid symmetric difference expression %s...%s\" :\n@@ -945,14 +960,14 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,\n \tdotdot = strstr(arg, \"^@\");\n \tif (dotdot && !dotdot[2]) {\n \t\t*dotdot = 0;\n-\t\tif (add_parents_only(revs, arg, flags))\n+\t\tif (add_parents_only(revs, arg, flags, flags_to_clear))\n \t\t\treturn 0;\n \t\t*dotdot = '^';\n \t}\n \tdotdot = strstr(arg, \"^!\");\n \tif (dotdot && !dotdot[2]) {\n \t\t*dotdot = 0;\n-\t\tif (!add_parents_only(revs, arg, flags ^ UNINTERESTING))\n+\t\tif (!add_parents_only(revs, arg, flags ^ UNINTERESTING, flags_to_clear))\n \t\t\t*dotdot = '^';\n \t}\n \n@@ -965,7 +980,17 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,\n \t\treturn -1;\n \tif (!cant_be_filename)\n \t\tverify_non_filename(revs->prefix, arg);\n-\tobject = get_reference(revs, arg, sha1, flags ^ local_flags);\n+\n+\tobject = parse_object(sha1); \n+\tif (!object)\n+\t    die(\"bad object %s\", arg);\n+\t\n+\tobject_deref = deref_tag(object, NULL, 0); \n+\tif (object_deref && object_deref->type == OBJ_COMMIT)\n+\t    if (flags_to_clear)\n+\t\tclear_commit_marks((struct commit *) object_deref, flags_to_clear); \n+\n+\tobject->flags |= flags ^ local_flags; \n \tadd_pending_object_with_mode(revs, object, arg, mode);\n \treturn 0;\n }\ndiff --git a/revision.h b/revision.h\nindex a14deef..36f78bb 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -143,6 +143,7 @@ extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ct\n \t\t\t\t const struct option *options,\n \t\t\t\t const char * const usagestr[]);\n extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);\n+extern int handle_revision_arg_clearing_flags(const char *arg, struct rev_info *revs,int flags, int flags_to_clear, int cant_be_filename);\n \n extern int prepare_revision_walk(struct rev_info *revs);\n extern struct commit *get_revision(struct rev_info *revs);\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 0f04b2e..9e49714 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -91,6 +91,80 @@ test_expect_success 'fetch without wildcard' '\n \t)\n '\n \n+\n+test_expect_success 'fetch --summary branch update' '\n+\tmk_empty &&\n+\t(\n+\t\tcd testrepo &&\n+\n+\t\tgit fetch  .. refs/heads/master:refs/remotes/origin/master &&\n+\n+\t\tgit update-ref refs/remotes/origin/master refs/remotes/origin/master^ &&\n+\n+\t\tgit fetch --summary .. refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l) &&\n+\n+\t\t  egrep  \"^    > [a-fA-F0-9]+ second$\" stderr &&\n+\t\t! egrep  \"^    > [a-fA-F0-9]+ repo$\" stderr\n+\t)\n+\t'\n+\n+test_expect_success 'fetch --summary bad ref' '\n+\tmk_empty &&\n+\t(\n+\t\tcd testrepo &&\n+\t\t! git fetch --summary .. foo:bar 2>stderr  &&\n+\t\t! grep \"seg\" stderr\n+\t)\n+\t'\n+\n+test_expect_success 'fetch --summary new branch' '\n+\tmk_empty &&\n+\t(\n+\t\tcd testrepo &&\n+\t\tgit fetch --summary .. refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l) &&\n+\n+\t\tegrep  \"^    > [a-fA-F0-9]+ second$\" stderr &&\n+\t\tegrep  \"^    > [a-fA-F0-9]+ repo$\" stderr\n+\t)\n+        '\n+\n+test_expect_success 'fetch --summary forced update' '\n+\tmk_empty &&\n+\t(\n+\t\tcd testrepo &&\n+\t\tgit fetch .. refs/heads/master:refs/remotes/origin/master &&\n+\n+\t\tgit checkout refs/remotes/origin/master^ &&\n+\t\t: >path3 &&\n+\t\tgit add path3 &&\n+\t\ttest_tick &&\n+\t\tgit commit -a -m third &&\n+\t\tgit update-ref refs/remotes/origin/master HEAD &&\n+\n+\t\tgit fetch .. -f --summary refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l) &&\n+\n+\t\tegrep  \"^    < [a-fA-F0-9]+ third$\" stderr &&\n+\t\tegrep  \"^    > [a-fA-F0-9]+ second$\" stderr\n+\t)\n+\n+'\n+\n test_expect_success 'fetch with wildcard' '\n \tmk_empty &&\n \t(\n@@ -153,6 +227,68 @@ test_expect_success 'push without wildcard' '\n \t)\n '\n \n+test_expect_success 'push --summary bad ref' '\n+\tmk_empty &&\n+\t! git push --summary testrepo foo:bar 2>stderr  &&\n+\t! grep \"seg\" stderr\n+'\n+\n+test_expect_success 'push --summary new branch' '\n+\tmk_empty &&\n+\n+\tgit push --summary testrepo refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\t(\n+\t\tcd testrepo &&\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l)\n+\t) &&\n+\n+\tegrep  \"^    > [a-fA-F0-9]+ second$\" stderr &&\n+\tegrep  \"^    > [a-fA-F0-9]+ repo$\" stderr\n+'\n+\n+test_expect_success 'push --summary branch update' '\n+\tmk_empty &&\n+\n+\tgit push testrepo refs/heads/master:refs/remotes/origin/master &&\n+\n+\tgit --git-dir testrepo/.git update-ref refs/remotes/origin/master refs/remotes/origin/master^ &&\n+\n+\tgit push --summary testrepo refs/heads/master:refs/remotes/origin/master 2>stderr &&\n+\t(\n+\t\tcd testrepo &&\n+\t\tr=$(git show-ref -s --verify refs/remotes/origin/master) &&\n+\t\ttest \"z$r\" = \"z$the_commit\" &&\n+\n+\t\ttest 1 = $(git for-each-ref refs/remotes/origin | wc -l)\n+\t) &&\n+\n+\t  egrep  \"^    > [a-fA-F0-9]+ second$\" stderr &&\n+\t! egrep  \"^    > [a-fA-F0-9]+ repo$\" stderr\n+'\n+\n+\n+test_expect_success 'push --summary forced update' '\n+\tmk_empty &&\n+\n+\tgit push testrepo refs/heads/master:refs/remotes/origin/master &&\n+\n+\tgit checkout master^ &&\n+\t: >path3 &&\n+\tgit add path3 &&\n+\ttest_tick &&\n+\tgit commit -a -m third &&\n+\n+\tgit push --summary -f testrepo HEAD:refs/remotes/origin/master 2>stderr &&\n+\n+\tegrep  \"^    < [a-fA-F0-9]+ second$\" stderr &&\n+\tegrep  \"^    > [a-fA-F0-9]+ third$\" stderr &&\n+\n+\tgit checkout master\n+'\n+\n test_expect_success 'push with wildcard' '\n \tmk_empty &&\n \ndiff --git a/transport.c b/transport.c\nindex 3846aac..1e3fa7a 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -8,6 +8,7 @@\n #include \"bundle.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"builtin.h\"\n #include \"branch.h\"\n \n /* rsync support */\n@@ -642,17 +643,20 @@ static const char *status_abbrev(unsigned char sha1[20])\n \treturn find_unique_abbrev(sha1, DEFAULT_ABBREV);\n }\n \n-static void print_ok_ref_status(struct ref *ref, int porcelain)\n+static void print_ok_ref_status(struct ref *ref, int porcelain, int summary)\n {\n+\tchar quickref[84];\n+\tint summary_impossible = 0;\n+\n \tif (ref->deletion)\n \t\tprint_ref_status('-', \"[deleted]\", ref, NULL, NULL, porcelain);\n-\telse if (is_null_sha1(ref->old_sha1))\n+\telse if (is_null_sha1(ref->old_sha1)) {\n \t\tprint_ref_status('*',\n \t\t\t(!prefixcmp(ref->name, \"refs/tags/\") ? \"[new tag]\" :\n \t\t\t\"[new branch]\"),\n \t\t\tref, ref->peer_ref, NULL, porcelain);\n-\telse {\n-\t\tchar quickref[84];\n+\t\tstrcpy(quickref, status_abbrev(ref->new_sha1));\n+\t} else {\n \t\tchar type;\n \t\tconst char *msg;\n \n@@ -661,6 +665,8 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \t\t\tstrcat(quickref, \"...\");\n \t\t\ttype = '+';\n \t\t\tmsg = \"forced update\";\n+\t\t\tif (!lookup_commit_reference_gently(ref->old_sha1, 1))\n+\t\t\t\tsummary_impossible = 1;\n \t\t} else {\n \t\t\tstrcat(quickref, \"..\");\n \t\t\ttype = ' ';\n@@ -670,9 +676,17 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \n \t\tprint_ref_status(type, quickref, ref, ref->peer_ref, msg, porcelain);\n \t}\n+\n+\tif (summary) {\n+\t\tif (summary_impossible) {\n+\t\t\tfprintf(stderr, \"    %s is unavailable\\n\", status_abbrev(ref->old_sha1));\n+\t\t} else {\n+\t\t\tprint_summary_for_push_or_fetch(quickref, summary);\n+\t\t}\n+\t}\n }\n \n-static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)\n+static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain, int summary)\n {\n \tif (!count)\n \t\tfprintf(stderr, \"To %s\\n\", dest);\n@@ -704,7 +718,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n \t\t\t\t\t\t \"remote failed to report status\", porcelain);\n \t\tbreak;\n \tcase REF_STATUS_OK:\n-\t\tprint_ok_ref_status(ref, porcelain);\n+\t\tprint_ok_ref_status(ref, porcelain, summary);\n \t\tbreak;\n \t}\n \n@@ -712,7 +726,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n }\n \n static void print_push_status(const char *dest, struct ref *refs,\n-\t\t\t      int verbose, int porcelain, int * nonfastforward)\n+\t\t\t\t\t\t\t  int verbose, int porcelain, int summary, int *nonfastforward)\n {\n \tstruct ref *ref;\n \tint n = 0;\n@@ -720,19 +734,19 @@ static void print_push_status(const char *dest, struct ref *refs,\n \tif (verbose) {\n \t\tfor (ref = refs; ref; ref = ref->next)\n \t\t\tif (ref->status == REF_STATUS_UPTODATE)\n-\t\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \t}\n \n \tfor (ref = refs; ref; ref = ref->next)\n \t\tif (ref->status == REF_STATUS_OK)\n-\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \n \t*nonfastforward = 0;\n \tfor (ref = refs; ref; ref = ref->next) {\n \t\tif (ref->status != REF_STATUS_NONE &&\n \t\t    ref->status != REF_STATUS_UPTODATE &&\n \t\t    ref->status != REF_STATUS_OK)\n-\t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n+\t\t\tn += print_one_push_status(ref, dest, n, porcelain, summary);\n \t\tif (ref->status == REF_STATUS_REJECT_NONFASTFORWARD)\n \t\t\t*nonfastforward = 1;\n \t}\n@@ -1014,8 +1028,8 @@ int transport_set_option(struct transport *transport,\n }\n \n int transport_push(struct transport *transport,\n-\t\t   int refspec_nr, const char **refspec, int flags,\n-\t\t   int *nonfastforward)\n+\t\t\t\t   int refspec_nr, const char **refspec,\n+\t\t\t\t   int flags, int summary, int *nonfastforward)\n {\n \t*nonfastforward = 0;\n \tverify_remote_names(refspec_nr, refspec);\n@@ -1058,8 +1072,8 @@ int transport_push(struct transport *transport,\n \n \t\tif (!quiet || err)\n \t\t\tprint_push_status(transport->url, remote_refs,\n-\t\t\t\t\tverbose | porcelain, porcelain,\n-\t\t\t\t\tnonfastforward);\n+\t\t\t\t\t\t\t  verbose | porcelain, porcelain,\n+\t\t\t\t\t\t\t  summary, nonfastforward);\n \n \t\tif (flags & TRANSPORT_PUSH_SET_UPSTREAM)\n \t\t\tset_upstreams(transport, remote_refs, pretend);\ndiff --git a/transport.h b/transport.h\nindex 7cea5cc..f87b2e9 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -124,9 +124,10 @@ int transport_set_option(struct transport *transport, const char *name,\n \t\t\t const char *value);\n \n int transport_push(struct transport *connection,\n-\t\t   int refspec_nr, const char **refspec, int flags,\n+\t\t   int refspec_nr, const char **refspec, int flags, int summary,\n \t\t   int * nonfastforward);\n \n+\n const struct ref *transport_get_remote_refs(struct transport *transport);\n \n int transport_fetch_refs(struct transport *transport, struct ref *refs);\n-- \n1.6.3.3.415.gbe1e\n"},{"id":"133099","messageId":"20100130075129.GA27702@Knoppix","threadId":"20011","inReplyTo":"7vsk9oysds.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v6] add --summary option to git-push and git-fetch","fromName":"Ilari Liusvaara","fromEmail":"ilari.liusvaara@elisanet.fi","sentAt":"2010-01-30T07:51:30Z","receivedAt":"2010-01-30T07:51:30Z","isPatch":true,"sender":{"key":"ilari.liusvaara@elisanet.fi","avatar":null},"body":"On Fri, Jan 29, 2010 at 11:16:31PM -0800, Junio C Hamano wrote:\n \n> As I said in my review during the earlier rounds, I do not know if it is\n> safe to use the flags and do the traversal inside this same process.  You\n> may be clearing the flags to protect your traversal (one per branch) from\n> stepping on each other, but how would this affect the use of object flags\n> in existing parts of the \"push\" machinery?  Is the reasoning that even if\n> push calls into traversal code and after it walked the commit ancestry for\n> its own purpose, your addition will clear the flags and existing code will\n> never look at object flags again, so this new code is free to use them and\n> all is Ok?  As long as you made sure that nobody looks at object flags you\n> modified, then I am fine with that---I just don't know if that is what is\n> happening here, and that is why I am asking.\n> \n> I'd need help from the usual \"transport\" suspects for this patch.\n\nWell, I can say smart transports implemented by remote helpers are similar\nto ssh://&co (no surprise, they connect differently, but use the same underlying\nclient code). Furthermore, actual remote helper stub code doesn't seem to play\nwith revisions.\n\nAnd the actual remote helper parts seem to use clean memory image anyway\n(they exec).\n\nSo that leaves the following:\n- git:// \"layer 7\" (git://, ssh://, file:// & co.[*])\n- rsync:// (third-class anyway)\n\nAlso, what about multiple-URL case? Don't know if there are problems, but it\nseems to be quite rarely tested...\n\n[*] OTOH, this is extremely heavily used code, so breakages here will usually\nbe pretty visible.\n\n-Ilari\n"},{"id":"133102","messageId":"7vd40sxbli.fsf@alter.siamese.dyndns.org","threadId":"20011","inReplyTo":"20100130075129.GA27702@Knoppix","subject":"Re: [PATCH v6] add --summary option to git-push and git-fetch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-30T08:04:25Z","receivedAt":"2010-01-30T08:04:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:\n\n> Also, what about multiple-URL case? Don't know if there are problems, but it\n> seems to be quite rarely tested...\n\nPushing to more than one deliberately chooses to fork for each remote IIRC\nto avoid any funnies.\n\n> [*] OTOH, this is extremely heavily used code, so breakages here will usually\n> be pretty visible.\n\nI'd actually like to avoid anybody being hit.\n\nThanks for a quick response.\n"},{"id":"133106","messageId":"20100130085750.GA27906@Knoppix","threadId":"20011","inReplyTo":"7vd40sxbli.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v6] add --summary option to git-push and git-fetch","fromName":"Ilari Liusvaara","fromEmail":"ilari.liusvaara@elisanet.fi","sentAt":"2010-01-30T08:57:50Z","receivedAt":"2010-01-30T08:57:50Z","isPatch":true,"sender":{"key":"ilari.liusvaara@elisanet.fi","avatar":null},"body":"On Sat, Jan 30, 2010 at 12:04:25AM -0800, Junio C Hamano wrote:\n> Ilari Liusvaara <ilari.liusvaara@elisanet.fi> writes:\n> \n> > Also, what about multiple-URL case? Don't know if there are problems, but it\n> > seems to be quite rarely tested...\n> \n> Pushing to more than one deliberately chooses to fork for each remote IIRC\n> to avoid any funnies.\n\nI don't see any forking in the code. It redoes transport_get() (transports\ncan't really be reused...) and in the end does transport_disconnect().\n\nAnd besides, what it did with internal protocol following remote helper\ndidn't look very much like what would happen if it forked...\n\n-Ilari\n"},{"id":"133179","messageId":"be6fef0d1001310404n204ffa31g3cf0a6f170507b3e@mail.gmail.com","threadId":"20011","inReplyTo":"20100130020548.GA29343@cthulhu","subject":"Re: [PATCH v6] add --summary option to git-push and git-fetch","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-01-31T12:04:46Z","receivedAt":"2010-01-31T12:04:46Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Hi,\n\nOn Sat, Jan 30, 2010 at 10:05 AM, Larry D'Anna <larry@elder-gods.org> wrote:\n> --summary will cause git-push to output a one-line of each commit pushed.\n> --summary=n will display at most n commits for each ref pushed.\n>\n> $ git push --dry-run --summary origin :\n> To /home/larry/gitsandbox/a\n>   80f0e50..5593a38  master -> master\n>    > 5593a38 foo\n>    > 81c03f8 bar\n>\n> Fetch works the same way.\n\nI'm sorry for being late to this discussion; I see the time and work\nyou've put in this and all the previous revisions. But I do have an\nobjection to implementing this behaviour using the option --summary,\non the grounds of UI-consistency.\n\nI believe git users are already familiar with --summary in git-diff\nand git-log, and it might confuse them when the output of --summary\nfor git-push and git-fetch looks different.\n\nAlso, I wonder what is the motivation behind displaying this\ninformation. Perhaps you are including this to produce output for an\nIDE? If this is included upstream, then I believe this is the first\ninstance of such verbose information being displayed. Even a\nfast-forward merge (the closest I can think of that matches this)\ndoesn't show this - I still have to do git log rev1^..rev2 to see.the\nintervening revisions (inclusive).\n\n-- \nCheers,\nRay Chuan\n"},{"id":"133207","messageId":"alpine.LNX.2.00.1001311846180.14365@iabervon.org","threadId":"20011","inReplyTo":"7vsk9oysds.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v6] add --summary option to git-push and git-fetch","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2010-02-01T00:34:24Z","receivedAt":"2010-02-01T00:34:24Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Fri, 29 Jan 2010, Junio C Hamano wrote:\n\n> As I said in my review during the earlier rounds, I do not know if it is\n> safe to use the flags and do the traversal inside this same process.  You\n> may be clearing the flags to protect your traversal (one per branch) from\n> stepping on each other, but how would this affect the use of object flags\n> in existing parts of the \"push\" machinery?  Is the reasoning that even if\n> push calls into traversal code and after it walked the commit ancestry for\n> its own purpose, your addition will clear the flags and existing code will\n> never look at object flags again, so this new code is free to use them and\n> all is Ok?  As long as you made sure that nobody looks at object flags you\n> modified, then I am fine with that---I just don't know if that is what is\n> happening here, and that is why I am asking.\n> \n> I'd need help from the usual \"transport\" suspects for this patch.\n\nI'm pretty sure that the built-in transport implementations all clear the \nflags themselves before using them. The fetch side has to be able to fetch \ntwice in order to handle tags, and the push side has to be able to push to \nmultiple destinations. So both parts should be defending themselves \nagainst flags that are specificly confusing to that part.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"133208","messageId":"20100201005751.GA8322@cthulhu","threadId":"20011","inReplyTo":"7vsk9oysds.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v6] add --summary option to git-push and git-fetch","fromName":"Larry D'Anna","fromEmail":"larry@elder-gods.org","sentAt":"2010-02-01T00:57:51Z","receivedAt":"2010-02-01T00:57:51Z","isPatch":true,"sender":{"key":"larry@elder-gods.org","avatar":"https://avatars.githubusercontent.com/u/3013304?v=4"},"body":"* Junio C Hamano (gitster@pobox.com) [100130 02:16]:\n> Larry D'Anna <larry@elder-gods.org> writes:\n> > +\n> > +\tobject = parse_object(sha1); \n> > +\tif (!object)\n> > +\t    die(\"bad object %s\", arg);\n> > +\t\n> > +\tobject_deref = deref_tag(object, NULL, 0); \n> > +\tif (object_deref && object_deref->type == OBJ_COMMIT)\n> > +\t    if (flags_to_clear)\n> > +\t\tclear_commit_marks((struct commit *) object_deref, flags_to_clear); \n> > +\n> > +\tobject->flags |= flags ^ local_flags; \n> \n> This smells somewhat fishy---what is the reason this \"peel and mark\" needs\n> to be done only in this codepath, and none of the other callers of\n> get_reference() need a similar logic, for example?\n> \n> In general, why do you need to sprinkle clear-commit-marks all over the\n> place?  \n\nMy idea was to call call clear_commit_marks on the \"roots\" of the revision arg,\nand since handle_revision_arg looks up those roots in several different places,\ni had to put clear_commit_marks in each of those places.  the reason the patch\nis particularly ugly in this spot is that the other places where i put\nclear_commit_marks, I already had a struct commit *, but here i just had a\nobject that might be a tag.\n\n> This is not a rhetorical question (I haven't reviewed all the\n> codepath involved for quite some time), but naïvely it appears it would be\n> a lot simpler if you can let the existing code to do all the revision\n> parsing and preparation to add to the pending object array as usual, and\n> clear the flags from them before you let prepare_revision_walk() to start\n> traversing the commit, but you probably had some reason why that simpler\n> approach would not work and did it this way.  What am I missing?\n\nThe \"existing code\" being the caller of print_summary_for_push_or_fetch?  I\nsuppose I just wanted to keep the patches interference with update_local_ref to\na minimum, so I had it just grab the existing variable \"quickref\" out of that\nfunction, because that was all the info I really needed to print the summary.\n\nSo i guess you're saying that it would be better for update_local_ref and\nprint_summary_for_push_or_fetch to clear the flags, and just pass a rev_info for\nprint_summary_for_push_or_fetch instead of quickref?\n\n   --larry\n"},{"id":"133619","messageId":"20100204171619.GA9367@cthulhu","threadId":"20011","inReplyTo":"20100201005751.GA8322@cthulhu","subject":"Re: [PATCH v6] add --summary option to git-push and git-fetch","fromName":"Larry D'Anna","fromEmail":"larry@elder-gods.org","sentAt":"2010-02-04T17:16:19Z","receivedAt":"2010-02-04T17:16:19Z","isPatch":true,"sender":{"key":"larry@elder-gods.org","avatar":"https://avatars.githubusercontent.com/u/3013304?v=4"},"body":"* Larry D'Anna (larry@elder-gods.org) [100131 19:57]:\n> So i guess you're saying that it would be better for update_local_ref and\n> print_summary_for_push_or_fetch to clear the flags, and just pass a rev_info for\n> print_summary_for_push_or_fetch instead of quickref?\n\nSo, should I submit a version of the patch that does it this way?  Should it use\na subprocess?  Should the option be called something other than --summary?\nShould I just forget about it?\n\n  --larry\n"},{"id":"133622","messageId":"7vwrysdiaq.fsf@alter.siamese.dyndns.org","threadId":"20011","inReplyTo":"20100204171619.GA9367@cthulhu","subject":"Re: [PATCH v6] add --summary option to git-push and git-fetch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-04T17:25:49Z","receivedAt":"2010-02-04T17:25:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Larry D'Anna <larry@elder-gods.org> writes:\n\n> * Larry D'Anna (larry@elder-gods.org) [100131 19:57]:\n>> So i guess you're saying that it would be better for update_local_ref and\n>> print_summary_for_push_or_fetch to clear the flags, and just pass a rev_info for\n>> print_summary_for_push_or_fetch instead of quickref?\n>\n> So, should I submit a version of the patch that does it this way?  Should it use\n> a subprocess?  Should the option be called something other than --summary?\n\nI dunno.  If it delegated to a subprocess it would certainly be easier to\nreview and get convinced that the change won't affect object flags for\nother parts of the system in bad ways, but there obviously is a\nperformance downside.\n\nI vaguely recall there also were comments on the output format not being\nconsistent with output of similar nature from other parts of the system,\nbut I am not the one who is particularly interested in this feature, so\nI'll let you and the list decide.\n"},{"id":"133625","messageId":"7v3a1gdgxq.fsf@alter.siamese.dyndns.org","threadId":"20011","inReplyTo":"7vwrysdiaq.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v6] add --summary option to git-push and git-fetch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-02-04T17:55:13Z","receivedAt":"2010-02-04T17:55:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I dunno.  If it delegated to a subprocess it would certainly be easier to\n> review and get convinced that the change won't affect object flags for\n> other parts of the system in bad ways, but there obviously is a\n> performance downside.\n\nYou fundamentally cannot use the same \"summary\" logic for push and fetch,\nand it is especially true if you are doing it inside the same process, I\nthink.\n\nWhen you force a fetch, you will have the complete histories for both old\nand new, as you started from old (and I am assuming that you are fsck\nclean) and you successfully fetched new.  When you force a push, however,\nyou may already have the old in your object store, but there is no\nguarantee that you have the complete history leading to it (i.e. you may\nhave got the tip commit left by an earlier fetch done with a commit walker\nthat you interrupted in the middle).\n\nSo at the very least, your \"summary_impossible\" logic should work a lot\nharder than a single lookup-commit-reference-gently; it needs to walk the\nancestry until you hit some ref to prove that you have a complete history\nfor that commit, without dying.  Otherwise get_revision() loop inside\nprint_summary_for_push_or_fetch() would say \"oops -- I don't have the\nparent commit\" when it tries to call add_parents_to_list() and die.\n\nDoing the summary traversal inside a subprocess would simplify the\nhandling of the error for such a case, I guess.\n\nMy gut feeling from the beginning has been that a patch that touches\nrevision.c for this topic would add unacceptable cruft to the already\ncomplex logic in that library for no real gain.  Doing the traversal\ninside a subprocess would allay that worry as well ;-)\n"}]}