{"thread":{"id":"35814","subject":"Confusing git log --- First time bug submission please advise on best practices","startedAt":"2014-02-06T14:02:09Z","lastAt":"2014-03-25T22:30:43Z","messageCount":19,"participants":["Francis Stephens","Vincent van Ravesteijn","David Kastrup","Duy Nguyen","demerphq","Nguyễn Thái Ngọc Duy","Eric Sunshine","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"234366","messageId":"CAJaBJzJeeEKpaTVXv+LnZd49xjnDjU25y9i_3kKNOrRPVbP-wg@mail.gmail.com","threadId":"35814","inReplyTo":null,"subject":"Confusing git log --- First time bug submission please advise on best practices","fromName":"Francis Stephens","fromEmail":"francisstephens@gmail.com","sentAt":"2014-02-06T14:02:09Z","receivedAt":"2014-02-06T14:02:09Z","isPatch":false,"sender":{"key":"francisstephens@gmail.com","avatar":null},"body":"My co-worker has an inconsistent git log output. Please see that\nattached files for output (I've made a best effort to remove\nconfidential info from them).\n\nComparing the two log commands we can see that master and\noriginssh/master have a shared common commit at\n\n    <John Doe> (4 hours ago) d85832d\n  More pom fixes\n\nThe top commit for originssh/master and the second to top for master.\n\nI would expect that both logs would share an _identical_ history from\nthat commit onward. But the log for master contains the following\n\n  <Jeremy Doe> (27 hours ago) 239ea21  (my-work)\n  renamed class\n\n  <Jeremy Doe> (28 hours ago) 27750b2\n  Merge branch 'master' of\nhttp://githost.companyname-dev.local/trading-development/sports-container-framework\n\nand\n\n  <Jeremy Doe> (2 days ago) a933acb\n  white space changes\n\n  <Jeremy Doe> (2 days ago) b5e51e7\n  Merge branch 'master' of\nhttp://githost.companyname-dev.local/trading-development/sports-container-framework\n\n  <Jeremy Doe> (2 days ago) 3a0f787\n  removed public methods\n\n  <Jeremy Doe> (2 days ago) 4e91130\n  added the xml deserialisation\n\nNone of which appear in the originssh/master log. Is there a scenario\nin which this is expected. It was my understanding that any two\ncommits with the same hash have exactly the same history.\n\nThanks for your time.\n\n\n\n\n\nTechnical details:\n\nHe is using the Windows git client version 1.8.5.2.msysgit.0\nRunning on Windows 7\nEvery command was run through git bash\n\nBackground:\n\nHe previously had tortoise-git installed, but I had him uninstall it\nto solve some ssh problems\nWhile uninstalling tortoise-git we also reinstalled git - I don't know\nwhat the previous version of git was.\n\nOutput of\n\n    git branch -avv\n    * master                   6833fd4 Completed merge\n      my-work                  239ea21 renamed class\n      remotes/origin/HEAD      -> origin/master\n      remotes/origin/master    f269789 Cleaning up GIT step 1\n      remotes/originssh/master d85832d More pom fixes\n\nThe background on the originssh remote branch is that his origin uses\nan HTTP url which wasn't allowing him to push a large commit. I helped\nhim to create originssh to allow the push to be made.\n"},{"id":"234374","messageId":"CAAH6HY8unuytrKpEA-eisojUkG=X4D+o+vQDO5bC5YCSmusoHw@mail.gmail.com","threadId":"35814","inReplyTo":"CAJaBJzJeeEKpaTVXv+LnZd49xjnDjU25y9i_3kKNOrRPVbP-wg@mail.gmail.com","subject":"Re: Confusing git log --- First time bug submission please advise on best practices","fromName":"Vincent van Ravesteijn","fromEmail":"vfr@lyx.org","sentAt":"2014-02-06T16:08:16Z","receivedAt":"2014-02-06T16:08:16Z","isPatch":false,"sender":{"key":"vfr@lyx.org","avatar":"https://avatars.githubusercontent.com/u/687868?v=4"},"body":"On Thu, Feb 6, 2014 at 3:02 PM, Francis Stephens\n<francisstephens@gmail.com> wrote:\n>\n> My co-worker has an inconsistent git log output. Please see that\n> attached files for output (I've made a best effort to remove\n> confidential info from them).\n>\n> Comparing the two log commands we can see that master and\n> originssh/master have a shared common commit at\n>\n> <John Doe> (4 hours ago) d85832d\n> More pom fixes\n>\n> The top commit for originssh/master and the second to top for master.\n>\n> I would expect that both logs would share an _identical_ history from\n> that commit onward. But the log for master contains the following\n>\n> <Jeremy Doe> (27 hours ago) 239ea21 (my-work)\n> renamed class\n>\n> <Jeremy Doe> (28 hours ago) 27750b2\n> Merge branch 'master' of\n> http://githost.companyname-dev.local/trading-development/sports-container-framework\n>\n> and\n>\n> <Jeremy Doe> (2 days ago) a933acb\n> white space changes\n>\n> <Jeremy Doe> (2 days ago) b5e51e7\n> Merge branch 'master' of\n> http://githost.companyname-dev.local/trading-development/sports-container-framework\n>\n> <Jeremy Doe> (2 days ago) 3a0f787\n> removed public methods\n>\n> <Jeremy Doe> (2 days ago) 4e91130\n> added the xml deserialisation\n>\n> None of which appear in the originssh/master log. Is there a scenario\n> in which this is expected. It was my understanding that any two\n> commits with the same hash have exactly the same history.\n>\n> Thanks for your time.\n\nThe commits that are in the log for master and which are not in the\nlog for originssh/master are merged in at \"6833fd4 (HEAD, master);\nCompleted merge\".\n\nAs \"git log\" can only present the commits in a linear way, it shows\nthe commits from the ancentry of both parents of HEAD in a reverse\nchronological order. This means that the commits from the two\nancestries are mixed and commits that are shown after each other don't\nhave to be parent and child. See the documentation of \"git log\" and\nthe section \"Commit Ordering\": \"By default, the commits are shown in\nreverse chronological order.\"\n\nVincent\n"},{"id":"234375","messageId":"874n4cjk83.fsf@fencepost.gnu.org","threadId":"35814","inReplyTo":"CAAH6HY8unuytrKpEA-eisojUkG=X4D+o+vQDO5bC5YCSmusoHw@mail.gmail.com","subject":"Re: Confusing git log --- First time bug submission please advise on best practices","fromName":"David Kastrup","fromEmail":"dak@gnu.org","sentAt":"2014-02-06T16:10:04Z","receivedAt":"2014-02-06T16:10:04Z","isPatch":false,"sender":{"key":"dak@gnu.org","avatar":"https://avatars.githubusercontent.com/u/52141349?v=4"},"body":"Vincent van Ravesteijn <vfr@lyx.org> writes:\n\n> The commits that are in the log for master and which are not in the\n> log for originssh/master are merged in at \"6833fd4 (HEAD, master);\n> Completed merge\".\n>\n> As \"git log\" can only present the commits in a linear way, it shows\n> the commits from the ancentry of both parents of HEAD in a reverse\n> chronological order. This means that the commits from the two\n> ancestries are mixed and commits that are shown after each other don't\n> have to be parent and child. See the documentation of \"git log\" and\n> the section \"Commit Ordering\": \"By default, the commits are shown in\n> reverse chronological order.\"\n\ngit log --graph can help with getting a better picture.\n\n-- \nDavid Kastrup\n"},{"id":"234436","messageId":"CAJaBJzKy0b8MBuB_X_4n8E2=q=CyrpxOyFFrgv_2CQ19pkNmwQ@mail.gmail.com","threadId":"35814","inReplyTo":"874n4cjk83.fsf@fencepost.gnu.org","subject":"Re: Confusing git log --- First time bug submission please advise on best practices","fromName":"Francis Stephens","fromEmail":"francisstephens@gmail.com","sentAt":"2014-02-07T09:43:46Z","receivedAt":"2014-02-07T09:43:46Z","isPatch":false,"sender":{"key":"francisstephens@gmail.com","avatar":null},"body":"Thanks for your clear response. I can see where I went wrong now.\n\nOn Thu, Feb 6, 2014 at 4:10 PM, David Kastrup <dak@gnu.org> wrote:\n> Vincent van Ravesteijn <vfr@lyx.org> writes:\n>\n>> The commits that are in the log for master and which are not in the\n>> log for originssh/master are merged in at \"6833fd4 (HEAD, master);\n>> Completed merge\".\n>>\n>> As \"git log\" can only present the commits in a linear way, it shows\n>> the commits from the ancentry of both parents of HEAD in a reverse\n>> chronological order. This means that the commits from the two\n>> ancestries are mixed and commits that are shown after each other don't\n>> have to be parent and child. See the documentation of \"git log\" and\n>> the section \"Commit Ordering\": \"By default, the commits are shown in\n>> reverse chronological order.\"\n>\n> git log --graph can help with getting a better picture.\n>\n> --\n> David Kastrup\n"},{"id":"234437","messageId":"20140207102619.GA27616@lanh","threadId":"35814","inReplyTo":"CAJaBJzKy0b8MBuB_X_4n8E2=q=CyrpxOyFFrgv_2CQ19pkNmwQ@mail.gmail.com","subject":"Re: Confusing git log --- First time bug submission please advise on best practices","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-02-07T10:26:20Z","receivedAt":"2014-02-07T10:26:20Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Feb 07, 2014 at 09:43:46AM +0000, Francis Stephens wrote:\n> Thanks for your clear response. I can see where I went wrong now.\n\nMaybe something like this would help avoid confusion a bit in the\nfuture? This toy patch puts a horizontal line as a break between two\ncommits if they are not related, so we can clearly see linear commit\nsegments.\n\n--graph definitely helps, but it's too many threads for topic-based\ndevelopment model like git.git that I avoid it most of the time.\n\n-- 8< --\ndiff --git a/log-tree.c b/log-tree.c\nindex 08970bf..7841bf2 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -795,6 +795,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log\n \n int log_tree_commit(struct rev_info *opt, struct commit *commit)\n {\n+\tstatic struct commit_list *last_parents;\n \tstruct log_info log;\n \tint shown;\n \n@@ -805,6 +806,17 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \tif (opt->line_level_traverse)\n \t\treturn line_log_print(opt, commit);\n \n+\tif (last_parents) {\n+\t\tstruct commit_list *p = last_parents;\n+\t\tint got_parent = 0;\n+\t\tfor (; p && !got_parent; p = p->next)\n+\t\t\tgot_parent = !hashcmp(p->item->object.sha1,\n+\t\t\t\t\t      commit->object.sha1);\n+\t\tif (!got_parent)\n+\t\t\tprintf(\"______________________________________________________________________\\n\");\n+\t\tfree_commit_list(last_parents);\n+\t\tlast_parents = NULL;\n+\t}\n \tshown = log_tree_diff(opt, commit, &log);\n \tif (!shown && opt->loginfo && opt->always_show_header) {\n \t\tlog.parent = NULL;\n@@ -813,5 +825,6 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \t}\n \topt->loginfo = NULL;\n \tmaybe_flush_or_die(stdout, \"stdout\");\n+\tlast_parents = copy_commit_list(commit->parents);\n \treturn shown;\n }\n-- 8< --\n\n> \n> On Thu, Feb 6, 2014 at 4:10 PM, David Kastrup <dak@gnu.org> wrote:\n> > Vincent van Ravesteijn <vfr@lyx.org> writes:\n> >\n> >> The commits that are in the log for master and which are not in the\n> >> log for originssh/master are merged in at \"6833fd4 (HEAD, master);\n> >> Completed merge\".\n> >>\n> >> As \"git log\" can only present the commits in a linear way, it shows\n> >> the commits from the ancentry of both parents of HEAD in a reverse\n> >> chronological order. This means that the commits from the two\n> >> ancestries are mixed and commits that are shown after each other don't\n> >> have to be parent and child. See the documentation of \"git log\" and\n> >> the section \"Commit Ordering\": \"By default, the commits are shown in\n> >> reverse chronological order.\"\n> >\n> > git log --graph can help with getting a better picture.\n"},{"id":"234438","messageId":"CANgJU+UcONQYAQ7K-_W7H_12JhfG+iuqYAU81xYMc5kLPXwn_Q@mail.gmail.com","threadId":"35814","inReplyTo":"20140207102619.GA27616@lanh","subject":"Re: Confusing git log --- First time bug submission please advise on best practices","fromName":"demerphq","fromEmail":"demerphq@gmail.com","sentAt":"2014-02-07T11:37:40Z","receivedAt":"2014-02-07T11:37:40Z","isPatch":false,"sender":{"key":"demerphq@gmail.com","avatar":null},"body":"On 7 February 2014 18:26, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Fri, Feb 07, 2014 at 09:43:46AM +0000, Francis Stephens wrote:\n>> Thanks for your clear response. I can see where I went wrong now.\n>\n> Maybe something like this would help avoid confusion a bit in the\n> future? This toy patch puts a horizontal line as a break between two\n> commits if they are not related, so we can clearly see linear commit\n> segments.\n\nFWIW, this would have saved a lot of head scratching at work over the years.\n\nI'd love to see this in place.\n\nYves\n\n\n-- \nperl -Mre=debug -e \"/just|another|perl|hacker/\"\n"},{"id":"234506","messageId":"1391867417-979-1-git-send-email-pclouds@gmail.com","threadId":"35814","inReplyTo":"CANgJU+UcONQYAQ7K-_W7H_12JhfG+iuqYAU81xYMc5kLPXwn_Q@mail.gmail.com","subject":"[PATCH] log: add --show-linear-break to help see non-linear history","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-02-08T13:50:17Z","receivedAt":"2014-02-08T13:50:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n This is a more serious attempt to make non-linear history more\n visible without --graph. It looks like this\n\n    commit e4ddb05720710213108cd13ddd5a115e12a6211d\n    Author: Andy Spencer <andy753421@gmail.com>\n    \n        tree_entry_interesting: match against all pathspecs\n    \n                        ..........\n    \n    commit a0332337be53f266682279c72a5e553986638c87\n    Author: Jeff King <peff@peff.net>\n    \n        t7700: do not use \"touch\" unnecessarily\n    \n    commit 088304bf73b9b4149e04d2246fe08a06eef6e795\n    Author: Jeff King <peff@peff.net>\n    \n        t7501: fix \"empty commit\" test with NO_PERL\n    \n                        ..........\n    \n    commit 74b4f7f27736f3e196a4eb3db41c68e37a6e2160\n    Author: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n    \n        tree-walk.c: ignore trailing slash on submodule in tree_entry_interesting()\n\n You can use fancier break bars from [1] instead of plain dots. I'm\n not sure it works with all combinations of rev-list. But at least\n log (with/out pathspec) and log --reverse work.\n \n [1] http://www.ascii-art.de/ascii/ab/border.txt\n\n Documentation/rev-list-options.txt |  7 +++++\n log-tree.c                         |  4 +++\n revision.c                         | 52 +++++++++++++++++++++++++++++++++++---\n revision.h                         |  6 +++++\n 4 files changed, 66 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex 03533af..a0780be 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -750,6 +750,13 @@ This enables parent rewriting, see 'History Simplification' below.\n This implies the `--topo-order` option by default, but the\n `--date-order` option may also be specified.\n \n+--show-linear-break[=<break bar>]::\n+\tWhen --graph is not used, all history branches are flatten and\n+\tcould be hard to see that the two consecutive commits do not\n+\tbelong to a linear branch. This option puts a break bar in\n+\tbetween them in that case. If `<break bar>` is specified, it\n+\tis the string that will be shown instead of the default one.\n+\n ifdef::git-rev-list[]\n --count::\n \tPrint a number stating how many commits would have been\ndiff --git a/log-tree.c b/log-tree.c\nindex 08970bf..17862f6 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -805,12 +805,16 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \tif (opt->line_level_traverse)\n \t\treturn line_log_print(opt, commit);\n \n+\tif (opt->track_linear && !opt->linear && !opt->reverse_output_stage)\n+\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n \tshown = log_tree_diff(opt, commit, &log);\n \tif (!shown && opt->loginfo && opt->always_show_header) {\n \t\tlog.parent = NULL;\n \t\tshow_log(opt);\n \t\tshown = 1;\n \t}\n+\tif (opt->track_linear && !opt->linear && opt->reverse_output_stage)\n+\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n \topt->loginfo = NULL;\n \tmaybe_flush_or_die(stdout, \"stdout\");\n \treturn shown;\ndiff --git a/revision.c b/revision.c\nindex a0df72f..ff3107f 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1836,6 +1836,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t\trevs->notes_opt.use_default_notes = 1;\n \t} else if (!strcmp(arg, \"--show-signature\")) {\n \t\trevs->show_signature = 1;\n+\t} else if (!strcmp(arg, \"--show-linear-break\")) {\n+\t\trevs->track_linear = 1;\n+\t\trevs->break_bar = \"                    ..........\";\n+\t} else if (starts_with(arg, \"--show-linear-break=\")) {\n+\t\trevs->track_linear = 1;\n+\t\trevs->break_bar = xstrdup(arg + 20);\n \t} else if (starts_with(arg, \"--show-notes=\") ||\n \t\t   starts_with(arg, \"--notes=\")) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n@@ -2901,6 +2907,32 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)\n \treturn action;\n }\n \n+define_commit_slab(saved_linear, int);\n+\n+static void track_linear(struct rev_info *revs, struct commit *commit)\n+{\n+\tstruct commit_list *p = revs->previous_parents;\n+\n+\tif (p) {\n+\t\tint got_parent = 0;\n+\t\tfor (; p && !got_parent; p = p->next)\n+\t\t\tgot_parent = !hashcmp(p->item->object.sha1,\n+\t\t\t\t\t      commit->object.sha1);\n+\t\trevs->linear = got_parent;\n+\t\tfree_commit_list(revs->previous_parents);\n+\t} else\n+\t\trevs->linear = 1;\n+\tif (revs->reverse) {\n+\t\tif (!revs->saved_linear_slab) {\n+\t\t\trevs->saved_linear_slab = xmalloc(sizeof(struct saved_linear));\n+\t\t\tinit_saved_linear(revs->saved_linear_slab);\n+\t\t}\n+\n+\t\t*saved_linear_at(revs->saved_linear_slab, commit) = revs->linear;\n+\t}\n+\trevs->previous_parents = copy_commit_list(commit->parents);\n+}\n+\n static struct commit *get_revision_1(struct rev_info *revs)\n {\n \tif (!revs->commits)\n@@ -2933,6 +2965,9 @@ static struct commit *get_revision_1(struct rev_info *revs)\n \t\t\t\t    sha1_to_hex(commit->object.sha1));\n \t\t}\n \n+\t\tif (revs->track_linear)\n+\t\t\ttrack_linear(revs, commit);\n+\n \t\tswitch (simplify_commit(revs, commit)) {\n \t\tcase commit_ignore:\n \t\t\tcontinue;\n@@ -3106,14 +3141,25 @@ struct commit *get_revision(struct rev_info *revs)\n \t\trevs->reverse_output_stage = 1;\n \t}\n \n-\tif (revs->reverse_output_stage)\n-\t\treturn pop_commit(&revs->commits);\n+\tif (revs->reverse_output_stage) {\n+\t\tc = pop_commit(&revs->commits);\n+\t\tif (revs->track_linear)\n+\t\t\trevs->linear = *saved_linear_at(revs->saved_linear_slab, c);\n+\t\treturn c;\n+\t}\n \n \tc = get_revision_internal(revs);\n \tif (c && revs->graph)\n \t\tgraph_update(revs->graph, c);\n-\tif (!c)\n+\tif (!c) {\n \t\tfree_saved_parents(revs);\n+\t\tif (revs->saved_linear_slab)\n+\t\t\tclear_saved_linear(revs->saved_linear_slab);\n+\t\tif (revs->previous_parents) {\n+\t\t\tfree_commit_list(revs->previous_parents);\n+\t\t\trevs->previous_parents = NULL;\n+\t\t}\n+\t}\n \treturn c;\n }\n \ndiff --git a/revision.h b/revision.h\nindex 88967d6..bffdc15 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -134,6 +134,7 @@ struct rev_info {\n \t\t\tuse_terminator:1,\n \t\t\tmissing_newline:1,\n \t\t\tdate_mode_explicit:1,\n+\t\t\ttrack_linear:1,\n \t\t\tpreserve_subject:1;\n \tunsigned int\tdisable_stdin:1;\n \tunsigned int\tleak_pending:1;\n@@ -195,6 +196,11 @@ struct rev_info {\n \n \t/* copies of the parent lists, for --full-diff display */\n \tstruct saved_parents *saved_parents_slab;\n+\n+\tstruct commit_list *previous_parents;\n+\tstruct saved_linear *saved_linear_slab;\n+\tchar *break_bar;\n+\tunsigned int linear;\n };\n \n extern int ref_excluded(struct string_list *, const char *path);\n-- \n1.8.5.2.240.g8478abd\n"},{"id":"236877","messageId":"1395060676-23144-1-git-send-email-pclouds@gmail.com","threadId":"35814","inReplyTo":"1391867417-979-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2] log: add --nonlinear-barrier to help see non-linear history","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-17T12:51:16Z","receivedAt":"2014-03-17T12:51:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n v2 renames the option name to --nonlinear-barrier and fixes using it\n with --dense. Best used with --no-merges to see patch series.\n\n Documentation/rev-list-options.txt |  7 ++++++\n log-tree.c                         |  4 +++\n revision.c                         | 51 +++++++++++++++++++++++++++++++++++---\n revision.h                         |  6 +++++\n 4 files changed, 65 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex 03533af..435aa2d 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -750,6 +750,13 @@ This enables parent rewriting, see 'History Simplification' below.\n This implies the `--topo-order` option by default, but the\n `--date-order` option may also be specified.\n \n+--nonlinear-barrier[=<barrier>]::\n+\tWhen --graph is not used, all history branches are flatten and\n+\tcould be hard to see that the two consecutive commits do not\n+\tbelong to a linear branch. This option puts a barrier in\n+\tbetween them in that case. If `<barrier>` is specified, it\n+\tis the string that will be shown instead of the default one.\n+\n ifdef::git-rev-list[]\n --count::\n \tPrint a number stating how many commits would have been\ndiff --git a/log-tree.c b/log-tree.c\nindex 08970bf..17862f6 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -805,12 +805,16 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \tif (opt->line_level_traverse)\n \t\treturn line_log_print(opt, commit);\n \n+\tif (opt->track_linear && !opt->linear && !opt->reverse_output_stage)\n+\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n \tshown = log_tree_diff(opt, commit, &log);\n \tif (!shown && opt->loginfo && opt->always_show_header) {\n \t\tlog.parent = NULL;\n \t\tshow_log(opt);\n \t\tshown = 1;\n \t}\n+\tif (opt->track_linear && !opt->linear && opt->reverse_output_stage)\n+\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n \topt->loginfo = NULL;\n \tmaybe_flush_or_die(stdout, \"stdout\");\n \treturn shown;\ndiff --git a/revision.c b/revision.c\nindex a68fde6..0a4849e 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1832,6 +1832,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t\trevs->notes_opt.use_default_notes = 1;\n \t} else if (!strcmp(arg, \"--show-signature\")) {\n \t\trevs->show_signature = 1;\n+\t} else if (!strcmp(arg, \"--nonlinear-barrier\")) {\n+\t\trevs->track_linear = 1;\n+\t\trevs->break_bar = \"                    ..........\";\n+\t} else if (starts_with(arg, \"--nonlinear-barrier=\")) {\n+\t\trevs->track_linear = 1;\n+\t\trevs->break_bar = xstrdup(arg + 20);\n \t} else if (starts_with(arg, \"--show-notes=\") ||\n \t\t   starts_with(arg, \"--notes=\")) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n@@ -2897,6 +2903,32 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)\n \treturn action;\n }\n \n+define_commit_slab(saved_linear, int);\n+\n+static void track_linear(struct rev_info *revs, struct commit *commit)\n+{\n+\tstruct commit_list *p = revs->previous_parents;\n+\n+\tif (p) {\n+\t\tint got_parent = 0;\n+\t\tfor (; p && !got_parent; p = p->next)\n+\t\t\tgot_parent = !hashcmp(p->item->object.sha1,\n+\t\t\t\t\t      commit->object.sha1);\n+\t\trevs->linear = got_parent;\n+\t\tfree_commit_list(revs->previous_parents);\n+\t} else\n+\t\trevs->linear = 1;\n+\tif (revs->reverse) {\n+\t\tif (!revs->saved_linear_slab) {\n+\t\t\trevs->saved_linear_slab = xmalloc(sizeof(struct saved_linear));\n+\t\t\tinit_saved_linear(revs->saved_linear_slab);\n+\t\t}\n+\n+\t\t*saved_linear_at(revs->saved_linear_slab, commit) = revs->linear;\n+\t}\n+\trevs->previous_parents = copy_commit_list(commit->parents);\n+}\n+\n static struct commit *get_revision_1(struct rev_info *revs)\n {\n \tif (!revs->commits)\n@@ -2936,6 +2968,8 @@ static struct commit *get_revision_1(struct rev_info *revs)\n \t\t\tdie(\"Failed to simplify parents of commit %s\",\n \t\t\t    sha1_to_hex(commit->object.sha1));\n \t\tdefault:\n+\t\t\tif (revs->track_linear)\n+\t\t\t\ttrack_linear(revs, commit);\n \t\t\treturn commit;\n \t\t}\n \t} while (revs->commits);\n@@ -3102,14 +3136,25 @@ struct commit *get_revision(struct rev_info *revs)\n \t\trevs->reverse_output_stage = 1;\n \t}\n \n-\tif (revs->reverse_output_stage)\n-\t\treturn pop_commit(&revs->commits);\n+\tif (revs->reverse_output_stage) {\n+\t\tc = pop_commit(&revs->commits);\n+\t\tif (revs->track_linear)\n+\t\t\trevs->linear = *saved_linear_at(revs->saved_linear_slab, c);\n+\t\treturn c;\n+\t}\n \n \tc = get_revision_internal(revs);\n \tif (c && revs->graph)\n \t\tgraph_update(revs->graph, c);\n-\tif (!c)\n+\tif (!c) {\n \t\tfree_saved_parents(revs);\n+\t\tif (revs->saved_linear_slab)\n+\t\t\tclear_saved_linear(revs->saved_linear_slab);\n+\t\tif (revs->previous_parents) {\n+\t\t\tfree_commit_list(revs->previous_parents);\n+\t\t\trevs->previous_parents = NULL;\n+\t\t}\n+\t}\n \treturn c;\n }\n \ndiff --git a/revision.h b/revision.h\nindex 88967d6..bffdc15 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -134,6 +134,7 @@ struct rev_info {\n \t\t\tuse_terminator:1,\n \t\t\tmissing_newline:1,\n \t\t\tdate_mode_explicit:1,\n+\t\t\ttrack_linear:1,\n \t\t\tpreserve_subject:1;\n \tunsigned int\tdisable_stdin:1;\n \tunsigned int\tleak_pending:1;\n@@ -195,6 +196,11 @@ struct rev_info {\n \n \t/* copies of the parent lists, for --full-diff display */\n \tstruct saved_parents *saved_parents_slab;\n+\n+\tstruct commit_list *previous_parents;\n+\tstruct saved_linear *saved_linear_slab;\n+\tchar *break_bar;\n+\tunsigned int linear;\n };\n \n extern int ref_excluded(struct string_list *, const char *path);\n-- \n1.9.0.40.gaa8c3ea\n"},{"id":"236898","messageId":"CAPig+cRDbnecPbefYQitROC+-QXCJF228hggf5kMGG8W-s_Lxw@mail.gmail.com","threadId":"35814","inReplyTo":"1395060676-23144-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2] log: add --nonlinear-barrier to help see non-linear history","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-17T19:09:26Z","receivedAt":"2014-03-17T19:09:26Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Mar 17, 2014 at 8:51 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  v2 renames the option name to --nonlinear-barrier and fixes using it\n>  with --dense. Best used with --no-merges to see patch series.\n>\n>  Documentation/rev-list-options.txt |  7 ++++++\n>  log-tree.c                         |  4 +++\n>  revision.c                         | 51 +++++++++++++++++++++++++++++++++++---\n>  revision.h                         |  6 +++++\n>  4 files changed, 65 insertions(+), 3 deletions(-)\n>\n> diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\n> index 03533af..435aa2d 100644\n> --- a/Documentation/rev-list-options.txt\n> +++ b/Documentation/rev-list-options.txt\n> @@ -750,6 +750,13 @@ This enables parent rewriting, see 'History Simplification' below.\n>  This implies the `--topo-order` option by default, but the\n>  `--date-order` option may also be specified.\n>\n> +--nonlinear-barrier[=<barrier>]::\n> +       When --graph is not used, all history branches are flatten and\n\ns/flatten/flattened/\n\n> +       could be hard to see that the two consecutive commits do not\n\ns/and could be/which can make it/\n\n> +       belong to a linear branch. This option puts a barrier in\n> +       between them in that case. If `<barrier>` is specified, it\n> +       is the string that will be shown instead of the default one.\n> +\n>  ifdef::git-rev-list[]\n>  --count::\n>         Print a number stating how many commits would have been\n"},{"id":"236910","messageId":"xmqqob14d0qv.fsf@gitster.dls.corp.google.com","threadId":"35814","inReplyTo":"1395060676-23144-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2] log: add --nonlinear-barrier to help see non-linear history","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-03-17T20:32:56Z","receivedAt":"2014-03-17T20:32:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  v2 renames the option name to --nonlinear-barrier and fixes using it\n>  with --dense. Best used with --no-merges to see patch series.\n\nI think that the earlier name \"show linear-break\" is more easily\nunderstood than the new name, but maybe that is just me.  It's not\nlike you are blocking something from going forward with a barrier,\nand internally it is called a \"break-bar\".\n\n>  Documentation/rev-list-options.txt |  7 ++++++\n>  log-tree.c                         |  4 +++\n>  revision.c                         | 51 +++++++++++++++++++++++++++++++++++---\n>  revision.h                         |  6 +++++\n>  4 files changed, 65 insertions(+), 3 deletions(-)\n>\n> diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\n> index 03533af..435aa2d 100644\n> --- a/Documentation/rev-list-options.txt\n> +++ b/Documentation/rev-list-options.txt\n> @@ -750,6 +750,13 @@ This enables parent rewriting, see 'History Simplification' below.\n>  This implies the `--topo-order` option by default, but the\n>  `--date-order` option may also be specified.\n>  \n> +--nonlinear-barrier[=<barrier>]::\n> +\tWhen --graph is not used, all history branches are flatten and\n\ns/flatten and/flattened and it/, perhaps?\n\n> +\tcould be hard to see that the two consecutive commits do not\n> +\tbelong to a linear branch. This option puts a barrier in\n> +\tbetween them in that case. If `<barrier>` is specified, it\n> +\tis the string that will be shown instead of the default one.\n> +\n>  ifdef::git-rev-list[]\n>  --count::\n>  \tPrint a number stating how many commits would have been\n> diff --git a/log-tree.c b/log-tree.c\n> index 08970bf..17862f6 100644\n> --- a/log-tree.c\n> +++ b/log-tree.c\n> @@ -805,12 +805,16 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n>  \tif (opt->line_level_traverse)\n>  \t\treturn line_log_print(opt, commit);\n>  \n> +\tif (opt->track_linear && !opt->linear && !opt->reverse_output_stage)\n> +\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n>  \tshown = log_tree_diff(opt, commit, &log);\n>  \tif (!shown && opt->loginfo && opt->always_show_header) {\n>  \t\tlog.parent = NULL;\n>  \t\tshow_log(opt);\n>  \t\tshown = 1;\n>  \t}\n> +\tif (opt->track_linear && !opt->linear && opt->reverse_output_stage)\n> +\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n\nEach time get_revision() returns a commit, it is inspected and\nopt->linear is updated, this function is called and we show the\nbreak in the single-strand-of-pearls if this is not a parent of the\ncommit we showed immediately before.  If we are showing the commit\nin reverse, we have to go the other way around, showing the commit\nand then the break.\n\nOK.  It makes sense to me.\n\n>  \topt->loginfo = NULL;\n>  \tmaybe_flush_or_die(stdout, \"stdout\");\n>  \treturn shown;\n\nDoes this new feature interact with -z format output in any way?\nShould it, and if so how?\n\n> diff --git a/revision.c b/revision.c\n> index a68fde6..0a4849e 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -1832,6 +1832,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n>  \t\trevs->notes_opt.use_default_notes = 1;\n>  \t} else if (!strcmp(arg, \"--show-signature\")) {\n>  \t\trevs->show_signature = 1;\n> +\t} else if (!strcmp(arg, \"--nonlinear-barrier\")) {\n> +\t\trevs->track_linear = 1;\n> +\t\trevs->break_bar = \"                    ..........\";\n> +\t} else if (starts_with(arg, \"--nonlinear-barrier=\")) {\n> +\t\trevs->track_linear = 1;\n> +\t\trevs->break_bar = xstrdup(arg + 20);\n>  \t} else if (starts_with(arg, \"--show-notes=\") ||\n>  \t\t   starts_with(arg, \"--notes=\")) {\n>  \t\tstruct strbuf buf = STRBUF_INIT;\n> @@ -2897,6 +2903,32 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)\n>  \treturn action;\n>  }\n>  \n> +define_commit_slab(saved_linear, int);\n> +\n> +static void track_linear(struct rev_info *revs, struct commit *commit)\n> +{\n> +\tstruct commit_list *p = revs->previous_parents;\n> +\n> +\tif (p) {\n> +\t\tint got_parent = 0;\n> +\t\tfor (; p && !got_parent; p = p->next)\n> +\t\t\tgot_parent = !hashcmp(p->item->object.sha1,\n> +\t\t\t\t\t      commit->object.sha1);\n> +\t\trevs->linear = got_parent;\n> +\t\tfree_commit_list(revs->previous_parents);\n> +\t} else\n> +\t\trevs->linear = 1;\n> +\tif (revs->reverse) {\n> +\t\tif (!revs->saved_linear_slab) {\n> +\t\t\trevs->saved_linear_slab = xmalloc(sizeof(struct saved_linear));\n> +\t\t\tinit_saved_linear(revs->saved_linear_slab);\n> +\t\t}\n> +\n> +\t\t*saved_linear_at(revs->saved_linear_slab, commit) = revs->linear;\n> +\t}\n> +\trevs->previous_parents = copy_commit_list(commit->parents);\n\nWe are showing commit, and the parents (after history\nsimplification) of the commit we showed before this commit is kept\nin previous-parents.  If we are one of them, we are showing\nlinearly, which makes sense.  While we are accumulating the output\nin the forward direction in preparation for later emitting them in\nreverse, we need to save away the linear-ness bit somewhere, and a\nslab is a logical place to save that, which also makes sense.  But\nwhy do you need a full int?  Doesn't an unsigned char wide enough?\n\nI also wondered if the saved-parents slab we already have can be\neasily reused for this, but it probably would not help.\n\nI do not quite understand the \"if we do not have previous parents\"\nbit, though.  Is it meant to trigger only at the very beginning?\n\nThanks.\n"},{"id":"236981","messageId":"CACsJy8BsEsyrcis=+2HyocYWi9WM2oHKtSFPXzSt5ji6zVgeXA@mail.gmail.com","threadId":"35814","inReplyTo":"xmqqob14d0qv.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2] log: add --nonlinear-barrier to help see non-linear history","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-18T11:46:28Z","receivedAt":"2014-03-18T11:46:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Mar 18, 2014 at 3:32 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  v2 renames the option name to --nonlinear-barrier and fixes using it\n>>  with --dense. Best used with --no-merges to see patch series.\n>\n> I think that the earlier name \"show linear-break\" is more easily\n> understood than the new name, but maybe that is just me.  It's not\n> like you are blocking something from going forward with a barrier,\n> and internally it is called a \"break-bar\".\n\nI'll change it back.\n\n>>       opt->loginfo = NULL;\n>>       maybe_flush_or_die(stdout, \"stdout\");\n>>       return shown;\n>\n> Does this new feature interact with -z format output in any way?\n\nHmm.. never thought of it. Right now it's part of the previous commit.\n\n> Should it, and if so how?\n\nNo idea.\n\n>> +define_commit_slab(saved_linear, int);\n>> +\n>> +static void track_linear(struct rev_info *revs, struct commit *commit)\n>> +{\n>> +     struct commit_list *p = revs->previous_parents;\n>> +\n>> +     if (p) {\n>> +             int got_parent = 0;\n>> +             for (; p && !got_parent; p = p->next)\n>> +                     got_parent = !hashcmp(p->item->object.sha1,\n>> +                                           commit->object.sha1);\n>> +             revs->linear = got_parent;\n>> +             free_commit_list(revs->previous_parents);\n>> +     } else\n>> +             revs->linear = 1;\n>> +     if (revs->reverse) {\n>> +             if (!revs->saved_linear_slab) {\n>> +                     revs->saved_linear_slab = xmalloc(sizeof(struct saved_linear));\n>> +                     init_saved_linear(revs->saved_linear_slab);\n>> +             }\n>> +\n>> +             *saved_linear_at(revs->saved_linear_slab, commit) = revs->linear;\n>> +     }\n>> +     revs->previous_parents = copy_commit_list(commit->parents);\n>\n> We are showing commit, and the parents (after history\n> simplification) of the commit we showed before this commit is kept\n> in previous-parents.  If we are one of them, we are showing\n> linearly, which makes sense.  While we are accumulating the output\n> in the forward direction in preparation for later emitting them in\n> reverse, we need to save away the linear-ness bit somewhere, and a\n> slab is a logical place to save that, which also makes sense.  But\n> why do you need a full int?  Doesn't an unsigned char wide enough?\n\nYes it is. Will change.\n\n> I also wondered if the saved-parents slab we already have can be\n> easily reused for this, but it probably would not help.\n\nThat could end up a maintenance nightmare. revision.c is complex\nenough as it is.\n\n> I do not quite understand the \"if we do not have previous parents\"\n> bit, though.  Is it meant to trigger only at the very beginning?\n\nOnly at the beginning.\n-- \nDuy\n"},{"id":"236998","messageId":"xmqqmwgn8gvf.fsf@gitster.dls.corp.google.com","threadId":"35814","inReplyTo":"CACsJy8BsEsyrcis=+2HyocYWi9WM2oHKtSFPXzSt5ji6zVgeXA@mail.gmail.com","subject":"Re: [PATCH v2] log: add --nonlinear-barrier to help see non-linear history","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-03-18T19:08:04Z","receivedAt":"2014-03-18T19:08:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> I do not quite understand the \"if we do not have previous parents\"\n>> bit, though.  Is it meant to trigger only at the very beginning?\n>\n> Only at the beginning.\n\nThen I am not sure !revs->previous_parents match that condition.\n\nWhat happens in a history with more than one root commits and the\nfirst one the revision traversal finds is one of the roots?  When\nshowing the second commit, after processing the root and storing its\nparent list, which is NULL, to revs->previous_parents, wouldn't it\nmistakenly think that it is the first round and fail to show the\nbreak line?\n"},{"id":"237142","messageId":"1395294254-941-1-git-send-email-pclouds@gmail.com","threadId":"35814","inReplyTo":"1395060676-23144-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 1/2] object.h: centralize object flag allocation","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-20T05:44:13Z","receivedAt":"2014-03-20T05:44:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"While the field \"flags\" is mainly used by the revision walker, it is\nalso used in many other places. Centralize the whole flag allocation to\none place for a better overview (and easier to move flags if we have\ntoo).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n The next step could be define (instead of document) all of them in\n a single place. But that may result in naming conflicts and stuff\n for little gain.\n\n bisect.c        |  3 +--\n builtin/blame.c |  2 +-\n bundle.c        |  1 +\n commit.c        |  2 +-\n fetch-pack.c    |  1 +\n http-push.c     |  3 +--\n object.h        | 13 +++++++++++++\n revision.h      |  1 +\n sha1_name.c     |  2 ++\n upload-pack.c   |  2 +-\n walker.c        |  1 +\n 11 files changed, 24 insertions(+), 7 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex 8448d27..d6e851d 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -21,8 +21,7 @@ static const char *argv_checkout[] = {\"checkout\", \"-q\", NULL, \"--\", NULL};\n static const char *argv_show_branch[] = {\"show-branch\", NULL, NULL};\n static const char *argv_update_ref[] = {\"update-ref\", \"--no-deref\", \"BISECT_HEAD\", NULL, NULL};\n \n-/* bits #0-15 in revision.h */\n-\n+/* Remember to update object flag allocation in object.h */\n #define COUNTED\t\t(1u<<16)\n \n /*\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex e5b5d71..88cb799 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -74,7 +74,7 @@ static unsigned blame_copy_score;\n #define BLAME_DEFAULT_MOVE_SCORE\t20\n #define BLAME_DEFAULT_COPY_SCORE\t40\n \n-/* bits #0..7 in revision.h, #8..11 used for merge_bases() in commit.c */\n+/* Remember to update object flag allocation in object.h */\n #define METAINFO_SHOWN\t\t(1u<<12)\n #define MORE_THAN_ONE_PATH\t(1u<<13)\n \ndiff --git a/bundle.c b/bundle.c\nindex a85e0e4..1222952 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -120,6 +120,7 @@ static int list_refs(struct ref_list *r, int argc, const char **argv)\n \treturn 0;\n }\n \n+/* Remember to update object flag allocation in object.h */\n #define PREREQ_MARK (1u<<16)\n \n int verify_bundle(struct bundle_header *header, int verbose)\ndiff --git a/commit.c b/commit.c\nindex 0f28902..f479331 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -721,7 +721,7 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so\n \n /* merge-base stuff */\n \n-/* bits #0..15 in revision.h */\n+/* Remember to update object flag allocation in object.h */\n #define PARENT1\t\t(1u<<16)\n #define PARENT2\t\t(1u<<17)\n #define STALE\t\t(1u<<18)\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex f061f1f..fd470e7 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -26,6 +26,7 @@ static int agent_supported;\n static struct lock_file shallow_lock;\n static const char *alternate_shallow_file;\n \n+/* Remember to update object flag allocation in object.h */\n #define COMPLETE\t(1U << 0)\n #define COMMON\t\t(1U << 1)\n #define COMMON_REF\t(1U << 2)\ndiff --git a/http-push.c b/http-push.c\nindex d4b40c9..f2c56c8 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -64,8 +64,7 @@ enum XML_Status {\n #define LOCK_TIME 600\n #define LOCK_REFRESH 30\n \n-/* bits #0-15 in revision.h */\n-\n+/* Remember to update object flag allocation in object.h */\n #define LOCAL    (1u<<16)\n #define REMOTE   (1u<<17)\n #define FETCHING (1u<<18)\ndiff --git a/object.h b/object.h\nindex 732bf4d..768490b 100644\n--- a/object.h\n+++ b/object.h\n@@ -26,6 +26,19 @@ struct object_array {\n #define OBJECT_ARRAY_INIT { 0, 0, NULL }\n \n #define TYPE_BITS   3\n+/*\n+ * object flag allocation:\n+ * revision.h:      0------10\n+ * fetch-pack.c:    0---4\n+ * walker.c:        0-2\n+ * upload-pack.c:             11---------19\n+ * builtin/blame.c:              12-13\n+ * bisect.c:                          16\n+ * bundle.c:                          16\n+ * http-push.c:                       16-19\n+ * commit.c:                          16-19\n+ * sha1_name.c:                            20\n+ */\n #define FLAG_BITS  27\n \n /*\ndiff --git a/revision.h b/revision.h\nindex 1eb94c1..0262bbd 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -7,6 +7,7 @@\n #include \"commit.h\"\n #include \"diff.h\"\n \n+/* Remember to update object flag allocation in object.h */\n #define SEEN\t\t(1u<<0)\n #define UNINTERESTING   (1u<<1)\n #define TREESAME\t(1u<<2)\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 6fca869..2b6322f 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -819,6 +819,8 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned l\n  * For future extension, ':/!' is reserved. If you want to match a message\n  * beginning with a '!', you have to repeat the exclamation mark.\n  */\n+\n+/* Remember to update object flag allocation in object.h */\n #define ONELINE_SEEN (1u<<20)\n \n static int handle_one_ref(const char *path,\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 9314c25..27df12f 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -17,7 +17,7 @@\n \n static const char upload_pack_usage[] = \"git upload-pack [--strict] [--timeout=<n>] <dir>\";\n \n-/* bits #0..7 in revision.h, #8..10 in commit.c */\n+/* Remember to update object flag allocation in object.h */\n #define THEY_HAVE\t(1u << 11)\n #define OUR_REF\t\t(1u << 12)\n #define WANTED\t\t(1u << 13)\ndiff --git a/walker.c b/walker.c\nindex 633596e..1dd86b8 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -60,6 +60,7 @@ static int process_tree(struct walker *walker, struct tree *tree)\n \treturn 0;\n }\n \n+/* Remember to update object flag allocation in object.h */\n #define COMPLETE\t(1U << 0)\n #define SEEN\t\t(1U << 1)\n #define TO_SCAN\t\t(1U << 2)\n-- \n1.9.0.201.g13d9d8b\n"},{"id":"237143","messageId":"1395294254-941-2-git-send-email-pclouds@gmail.com","threadId":"35814","inReplyTo":"1395294254-941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 2/2] log: add --show-linear-break to help see non-linear history","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-20T05:44:14Z","receivedAt":"2014-03-20T05:44:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Option explanation is in rev-list-options.txt. The interaction with -z\nis left undecided.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n * Revert back to the old option name --show-linear-break\n * Get rid of saved_linear, use another flag in struct object instead\n * Fix not showing the break bar after a root commit if the dag graph\n   has multiple roots\n * Make it work with --graph (although I don't really see the point of\n   using both at the same time)\n * Let the next contributor deal with -z\n\n Documentation/rev-list-options.txt |  7 ++++++\n log-tree.c                         |  9 ++++++++\n object.h                           |  2 +-\n revision.c                         | 45 +++++++++++++++++++++++++++++++++++---\n revision.h                         |  9 +++++++-\n 5 files changed, 67 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex 9a3da36..b813961 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -758,6 +758,13 @@ This enables parent rewriting, see 'History Simplification' below.\n This implies the `--topo-order` option by default, but the\n `--date-order` option may also be specified.\n \n+--show-linear-break[=<barrier>]::\n+\tWhen --graph is not used, all history branches are flattened\n+\twhich can make it hard to see that the two consecutive commits\n+\tdo not belong to a linear branch. This option puts a barrier\n+\tin between them in that case. If `<barrier>` is specified, it\n+\tis the string that will be shown instead of the default one.\n+\n ifdef::git-rev-list[]\n --count::\n \tPrint a number stating how many commits would have been\ndiff --git a/log-tree.c b/log-tree.c\nindex 5ce217d..c51a878 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -805,12 +805,21 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \tif (opt->line_level_traverse)\n \t\treturn line_log_print(opt, commit);\n \n+\tif (opt->track_linear && !opt->linear && !opt->reverse_output_stage) {\n+\t\tif (opt->graph)\n+\t\t\tgraph_show_padding(opt->graph);\n+\t\telse\n+\t\t\tputs(\"\\n\");\n+\t\tprintf(\"%s\\n\", opt->break_bar);\n+\t}\n \tshown = log_tree_diff(opt, commit, &log);\n \tif (!shown && opt->loginfo && opt->always_show_header) {\n \t\tlog.parent = NULL;\n \t\tshow_log(opt);\n \t\tshown = 1;\n \t}\n+\tif (opt->track_linear && !opt->linear && opt->reverse_output_stage)\n+\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n \topt->loginfo = NULL;\n \tmaybe_flush_or_die(stdout, \"stdout\");\n \treturn shown;\ndiff --git a/object.h b/object.h\nindex 768490b..9ee1959 100644\n--- a/object.h\n+++ b/object.h\n@@ -28,7 +28,7 @@ struct object_array {\n #define TYPE_BITS   3\n /*\n  * object flag allocation:\n- * revision.h:      0------10\n+ * revision.h:      0------10                 26\n  * fetch-pack.c:    0---4\n  * walker.c:        0-2\n  * upload-pack.c:             11---------19\ndiff --git a/revision.c b/revision.c\nindex 78b5c3a..b9afdce 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1831,6 +1831,18 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t\trevs->notes_opt.use_default_notes = 1;\n \t} else if (!strcmp(arg, \"--show-signature\")) {\n \t\trevs->show_signature = 1;\n+\t} else if (!strcmp(arg, \"--show-linear-break\") ||\n+\t\t   starts_with(arg, \"--show-linear-break=\")) {\n+\t\tif (starts_with(arg, \"--show-linear-break=\"))\n+\t\t\trevs->break_bar = xstrdup(arg + 20);\n+\t\telse\n+\t\t\trevs->break_bar = \"                    ..........\";\n+\t\trevs->track_linear = 1;\n+\t\t/*\n+\t\t * make track_linear() not a break bar before the\n+\t\t * first shown commit.\n+\t\t */\n+\t\tcommit_list_insert(NULL, &revs->previous_parents);\n \t} else if (starts_with(arg, \"--show-notes=\") ||\n \t\t   starts_with(arg, \"--notes=\")) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n@@ -2896,6 +2908,22 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)\n \treturn action;\n }\n \n+static void track_linear(struct rev_info *revs, struct commit *commit)\n+{\n+\tstruct commit_list *p;\n+\tfor (p = revs->previous_parents; p; p = p->next)\n+\t\tif (p->item == NULL || /* first commit */\n+\t\t    !hashcmp(p->item->object.sha1, commit->object.sha1))\n+\t\t\tbreak;\n+\trevs->linear = p != NULL;\n+\tif (revs->reverse) {\n+\t\tif (revs->linear)\n+\t\t\tcommit->object.flags |= TRACK_LINEAR;\n+\t}\n+\tfree_commit_list(revs->previous_parents);\n+\trevs->previous_parents = copy_commit_list(commit->parents);\n+}\n+\n static struct commit *get_revision_1(struct rev_info *revs)\n {\n \tif (!revs->commits)\n@@ -2935,6 +2963,8 @@ static struct commit *get_revision_1(struct rev_info *revs)\n \t\t\tdie(\"Failed to simplify parents of commit %s\",\n \t\t\t    sha1_to_hex(commit->object.sha1));\n \t\tdefault:\n+\t\t\tif (revs->track_linear)\n+\t\t\t\ttrack_linear(revs, commit);\n \t\t\treturn commit;\n \t\t}\n \t} while (revs->commits);\n@@ -3101,14 +3131,23 @@ struct commit *get_revision(struct rev_info *revs)\n \t\trevs->reverse_output_stage = 1;\n \t}\n \n-\tif (revs->reverse_output_stage)\n-\t\treturn pop_commit(&revs->commits);\n+\tif (revs->reverse_output_stage) {\n+\t\tc = pop_commit(&revs->commits);\n+\t\tif (revs->track_linear)\n+\t\t\trevs->linear = !!(c && c->object.flags & TRACK_LINEAR);\n+\t\treturn c;\n+\t}\n \n \tc = get_revision_internal(revs);\n \tif (c && revs->graph)\n \t\tgraph_update(revs->graph, c);\n-\tif (!c)\n+\tif (!c) {\n \t\tfree_saved_parents(revs);\n+\t\tif (revs->previous_parents) {\n+\t\t\tfree_commit_list(revs->previous_parents);\n+\t\t\trevs->previous_parents = NULL;\n+\t\t}\n+\t}\n \treturn c;\n }\n \ndiff --git a/revision.h b/revision.h\nindex 0262bbd..64503e9 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -19,7 +19,8 @@\n #define SYMMETRIC_LEFT\t(1u<<8)\n #define PATCHSAME\t(1u<<9)\n #define BOTTOM\t\t(1u<<10)\n-#define ALL_REV_FLAGS\t((1u<<11)-1)\n+#define TRACK_LINEAR\t(1u<<26)\n+#define ALL_REV_FLAGS\t(((1u<<11)-1) | TRACK_LINEAR)\n \n #define DECORATE_SHORT_REFS\t1\n #define DECORATE_FULL_REFS\t2\n@@ -138,6 +139,9 @@ struct rev_info {\n \t\t\tpreserve_subject:1;\n \tunsigned int\tdisable_stdin:1;\n \tunsigned int\tleak_pending:1;\n+\t/* --show-linear-break */\n+\tunsigned int\ttrack_linear:1,\n+\t\t\tlinear:1;\n \n \tenum date_mode date_mode;\n \n@@ -198,6 +202,9 @@ struct rev_info {\n \n \t/* copies of the parent lists, for --full-diff display */\n \tstruct saved_parents *saved_parents_slab;\n+\n+\tstruct commit_list *previous_parents;\n+\tconst char *break_bar;\n };\n \n extern int ref_excluded(struct string_list *, const char *path);\n-- \n1.9.0.201.g13d9d8b\n"},{"id":"237195","messageId":"xmqqha6sy955.fsf@gitster.dls.corp.google.com","threadId":"35814","inReplyTo":"1395294254-941-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 2/2] log: add --show-linear-break to help see non-linear history","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-03-20T19:15:02Z","receivedAt":"2014-03-20T19:15:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Option explanation is in rev-list-options.txt. The interaction with -z\n> is left undecided.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nThanks.\n\n>  * Revert back to the old option name --show-linear-break\n>  * Get rid of saved_linear, use another flag in struct object instead\n\nI cannot offhand say if I like this change or not.  A flag bit is a\nscarce and limited resource; commit slabs felt more suited for\nimplementation of corner case eye-candies.\n\n>  * Fix not showing the break bar after a root commit if the dag graph\n>    has multiple roots\n\nI definitely do not like the way a commit-list data structure is\nabused to hold a phoney element that points at a NULL with its item\npointer.  Allocate a single bit in revs that says \"I haven't done\nanything yet\" if you want to catch the \"first-ness\" without breaking\nwhat commit_list_insert() and friends are expecting to see---they\nnever expect to see a NULL asked to be on the list, AFAIK.\n\n>  * Make it work with --graph (although I don't really see the point of\n>    using both at the same time)\n\nI do not see the point, either.  I vaguely recall that the previous\niteration refused the combination at the option parser level, which\nI think would be the right thing to do.\n\n>  * Let the next contributor deal with -z\n\nThat is fine.\n"},{"id":"237249","messageId":"CACsJy8A1XZm1FanJ-QufOHsX44daVSEJA=g7VnA0bYS6uMzNVQ@mail.gmail.com","threadId":"35814","inReplyTo":"xmqqha6sy955.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v3 2/2] log: add --show-linear-break to help see non-linear history","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-21T01:02:30Z","receivedAt":"2014-03-21T01:02:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Mar 21, 2014 at 2:15 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>  * Get rid of saved_linear, use another flag in struct object instead\n>\n> I cannot offhand say if I like this change or not.  A flag bit is a\n> scarce and limited resource; commit slabs felt more suited for\n> implementation of corner case eye-candies.\n\nMy thinking was like this:\n\nOK an int for a flag is wasteful and Junio suggested that unsigned\nchar is used. But that still wastes 7 bits. So what if I rename it to\ncommit_flags and make it usable as a flag storage for other parts as\nwell? Wait don't we have some flags in struct object#flags. It turns\nout we have _7_ flags left that nobody touches. Let's take one. If we\nrun out of flags in future, we can bring back commit_flags slab,\nrearrange the flags and move rarely used ones to commit_flags.\n-- \nDuy\n"},{"id":"237652","messageId":"1395753807-23228-1-git-send-email-pclouds@gmail.com","threadId":"35814","inReplyTo":"1395294254-941-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 1/2] object.h: centralize object flag allocation","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-25T13:23:26Z","receivedAt":"2014-03-25T13:23:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"While the field \"flags\" is mainly used by the revision walker, it is\nalso used in many other places. Centralize the whole flag allocation to\none place for a better overview (and easier to move flags if we have\ntoo).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n bisect.c        |  3 +--\n builtin/blame.c |  2 +-\n bundle.c        |  1 +\n commit.c        |  2 +-\n fetch-pack.c    |  1 +\n http-push.c     |  3 +--\n object.h        | 13 +++++++++++++\n revision.h      |  1 +\n sha1_name.c     |  2 ++\n upload-pack.c   |  2 +-\n walker.c        |  1 +\n 11 files changed, 24 insertions(+), 7 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex 8448d27..d6e851d 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -21,8 +21,7 @@ static const char *argv_checkout[] = {\"checkout\", \"-q\", NULL, \"--\", NULL};\n static const char *argv_show_branch[] = {\"show-branch\", NULL, NULL};\n static const char *argv_update_ref[] = {\"update-ref\", \"--no-deref\", \"BISECT_HEAD\", NULL, NULL};\n \n-/* bits #0-15 in revision.h */\n-\n+/* Remember to update object flag allocation in object.h */\n #define COUNTED\t\t(1u<<16)\n \n /*\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex e5b5d71..88cb799 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -74,7 +74,7 @@ static unsigned blame_copy_score;\n #define BLAME_DEFAULT_MOVE_SCORE\t20\n #define BLAME_DEFAULT_COPY_SCORE\t40\n \n-/* bits #0..7 in revision.h, #8..11 used for merge_bases() in commit.c */\n+/* Remember to update object flag allocation in object.h */\n #define METAINFO_SHOWN\t\t(1u<<12)\n #define MORE_THAN_ONE_PATH\t(1u<<13)\n \ndiff --git a/bundle.c b/bundle.c\nindex a85e0e4..1222952 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -120,6 +120,7 @@ static int list_refs(struct ref_list *r, int argc, const char **argv)\n \treturn 0;\n }\n \n+/* Remember to update object flag allocation in object.h */\n #define PREREQ_MARK (1u<<16)\n \n int verify_bundle(struct bundle_header *header, int verbose)\ndiff --git a/commit.c b/commit.c\nindex 0f28902..f479331 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -721,7 +721,7 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so\n \n /* merge-base stuff */\n \n-/* bits #0..15 in revision.h */\n+/* Remember to update object flag allocation in object.h */\n #define PARENT1\t\t(1u<<16)\n #define PARENT2\t\t(1u<<17)\n #define STALE\t\t(1u<<18)\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 90d47da..eeee2bb 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -26,6 +26,7 @@ static int agent_supported;\n static struct lock_file shallow_lock;\n static const char *alternate_shallow_file;\n \n+/* Remember to update object flag allocation in object.h */\n #define COMPLETE\t(1U << 0)\n #define COMMON\t\t(1U << 1)\n #define COMMON_REF\t(1U << 2)\ndiff --git a/http-push.c b/http-push.c\nindex d4b40c9..f2c56c8 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -64,8 +64,7 @@ enum XML_Status {\n #define LOCK_TIME 600\n #define LOCK_REFRESH 30\n \n-/* bits #0-15 in revision.h */\n-\n+/* Remember to update object flag allocation in object.h */\n #define LOCAL    (1u<<16)\n #define REMOTE   (1u<<17)\n #define FETCHING (1u<<18)\ndiff --git a/object.h b/object.h\nindex 732bf4d..9918777 100644\n--- a/object.h\n+++ b/object.h\n@@ -26,6 +26,19 @@ struct object_array {\n #define OBJECT_ARRAY_INIT { 0, 0, NULL }\n \n #define TYPE_BITS   3\n+/*\n+ * object flag allocation:\n+ * revision.h:      0---------10\n+ * fetch-pack.c:    0---4\n+ * walker.c:        0-2\n+ * upload-pack.c:               11----------------19\n+ * builtin/blame.c:               12-13\n+ * bisect.c:                               16\n+ * bundle.c:                               16\n+ * http-push.c:                            16-----19\n+ * commit.c:                               16-----19\n+ * sha1_name.c:                                     20\n+ */\n #define FLAG_BITS  27\n \n /*\ndiff --git a/revision.h b/revision.h\nindex 1eb94c1..0262bbd 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -7,6 +7,7 @@\n #include \"commit.h\"\n #include \"diff.h\"\n \n+/* Remember to update object flag allocation in object.h */\n #define SEEN\t\t(1u<<0)\n #define UNINTERESTING   (1u<<1)\n #define TREESAME\t(1u<<2)\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 6fca869..2b6322f 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -819,6 +819,8 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned l\n  * For future extension, ':/!' is reserved. If you want to match a message\n  * beginning with a '!', you have to repeat the exclamation mark.\n  */\n+\n+/* Remember to update object flag allocation in object.h */\n #define ONELINE_SEEN (1u<<20)\n \n static int handle_one_ref(const char *path,\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 286a9ed..01de944 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -17,7 +17,7 @@\n \n static const char upload_pack_usage[] = \"git upload-pack [--strict] [--timeout=<n>] <dir>\";\n \n-/* bits #0..7 in revision.h, #8..10 in commit.c */\n+/* Remember to update object flag allocation in object.h */\n #define THEY_HAVE\t(1u << 11)\n #define OUR_REF\t\t(1u << 12)\n #define WANTED\t\t(1u << 13)\ndiff --git a/walker.c b/walker.c\nindex 633596e..1dd86b8 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -60,6 +60,7 @@ static int process_tree(struct walker *walker, struct tree *tree)\n \treturn 0;\n }\n \n+/* Remember to update object flag allocation in object.h */\n #define COMPLETE\t(1U << 0)\n #define SEEN\t\t(1U << 1)\n #define TO_SCAN\t\t(1U << 2)\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237653","messageId":"1395753807-23228-2-git-send-email-pclouds@gmail.com","threadId":"35814","inReplyTo":"1395753807-23228-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 2/2] log: add --show-linear-break to help see non-linear history","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2014-03-25T13:23:27Z","receivedAt":"2014-03-25T13:23:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Option explanation is in rev-list-options.txt. The interaction with -z\nis left undecided.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n On Fri, Mar 21, 2014 at 2:15 AM, Junio C Hamano <gitster@pobox.com> wrote:\n >>  * Get rid of saved_linear, use another flag in struct object instead\n >\n > I cannot offhand say if I like this change or not.  A flag bit is a\n > scarce and limited resource; commit slabs felt more suited for\n > implementation of corner case eye-candies.\n\n I leave it in bit 26. We can move it out when we run low on flag bits.\n\n >>  * Fix not showing the break bar after a root commit if the dag graph\n >>    has multiple roots\n >\n > I definitely do not like the way a commit-list data structure is\n > abused to hold a phoney element that points at a NULL with its item\n > pointer.  Allocate a single bit in revs that says \"I haven't done\n > anything yet\" if you want to catch the \"first-ness\" without breaking\n > what commit_list_insert() and friends are expecting to see---they\n > never expect to see a NULL asked to be on the list, AFAIK.\n\n Fixed.\n\n >>  * Make it work with --graph (although I don't really see the point of\n >>    using both at the same time)\n >\n > I do not see the point, either.  I vaguely recall that the previous\n > iteration refused the combination at the option parser level, which\n > I think would be the right thing to do.\n\n Fixed.\n\n Documentation/rev-list-options.txt |  7 ++++++\n log-tree.c                         |  4 ++++\n object.h                           |  2 +-\n revision.c                         | 48 +++++++++++++++++++++++++++++++++++---\n revision.h                         | 10 +++++++-\n 5 files changed, 66 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex 9a3da36..b813961 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -758,6 +758,13 @@ This enables parent rewriting, see 'History Simplification' below.\n This implies the `--topo-order` option by default, but the\n `--date-order` option may also be specified.\n \n+--show-linear-break[=<barrier>]::\n+\tWhen --graph is not used, all history branches are flattened\n+\twhich can make it hard to see that the two consecutive commits\n+\tdo not belong to a linear branch. This option puts a barrier\n+\tin between them in that case. If `<barrier>` is specified, it\n+\tis the string that will be shown instead of the default one.\n+\n ifdef::git-rev-list[]\n --count::\n \tPrint a number stating how many commits would have been\ndiff --git a/log-tree.c b/log-tree.c\nindex 5ce217d..cf2f86c 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -805,12 +805,16 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)\n \tif (opt->line_level_traverse)\n \t\treturn line_log_print(opt, commit);\n \n+\tif (opt->track_linear && !opt->linear && !opt->reverse_output_stage)\n+\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n \tshown = log_tree_diff(opt, commit, &log);\n \tif (!shown && opt->loginfo && opt->always_show_header) {\n \t\tlog.parent = NULL;\n \t\tshow_log(opt);\n \t\tshown = 1;\n \t}\n+\tif (opt->track_linear && !opt->linear && opt->reverse_output_stage)\n+\t\tprintf(\"\\n%s\\n\", opt->break_bar);\n \topt->loginfo = NULL;\n \tmaybe_flush_or_die(stdout, \"stdout\");\n \treturn shown;\ndiff --git a/object.h b/object.h\nindex 9918777..6e12f2c 100644\n--- a/object.h\n+++ b/object.h\n@@ -28,7 +28,7 @@ struct object_array {\n #define TYPE_BITS   3\n /*\n  * object flag allocation:\n- * revision.h:      0---------10\n+ * revision.h:      0---------10                                26\n  * fetch-pack.c:    0---4\n  * walker.c:        0-2\n  * upload-pack.c:               11----------------19\ndiff --git a/revision.c b/revision.c\nindex 78b5c3a..f834aa9 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1831,6 +1831,14 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t\trevs->notes_opt.use_default_notes = 1;\n \t} else if (!strcmp(arg, \"--show-signature\")) {\n \t\trevs->show_signature = 1;\n+\t} else if (!strcmp(arg, \"--show-linear-break\") ||\n+\t\t   starts_with(arg, \"--show-linear-break=\")) {\n+\t\tif (starts_with(arg, \"--show-linear-break=\"))\n+\t\t\trevs->break_bar = xstrdup(arg + 20);\n+\t\telse\n+\t\t\trevs->break_bar = \"                    ..........\";\n+\t\trevs->track_linear = 1;\n+\t\trevs->track_first_time = 1;\n \t} else if (starts_with(arg, \"--show-notes=\") ||\n \t\t   starts_with(arg, \"--notes=\")) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n@@ -1954,6 +1962,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg\n \t\t\tunkv[(*unkc)++] = arg;\n \t\treturn opts;\n \t}\n+\tif (revs->graph && revs->track_linear)\n+\t\tdie(\"--show-linear-break and --graph are incompatible\");\n \n \treturn 1;\n }\n@@ -2896,6 +2906,27 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)\n \treturn action;\n }\n \n+static void track_linear(struct rev_info *revs, struct commit *commit)\n+{\n+\tif (revs->track_first_time) {\n+\t\trevs->linear = 1;\n+\t\trevs->track_first_time = 0;\n+\t} else {\n+\t\tstruct commit_list *p;\n+\t\tfor (p = revs->previous_parents; p; p = p->next)\n+\t\t\tif (p->item == NULL || /* first commit */\n+\t\t\t    !hashcmp(p->item->object.sha1, commit->object.sha1))\n+\t\t\t\tbreak;\n+\t\trevs->linear = p != NULL;\n+\t}\n+\tif (revs->reverse) {\n+\t\tif (revs->linear)\n+\t\t\tcommit->object.flags |= TRACK_LINEAR;\n+\t}\n+\tfree_commit_list(revs->previous_parents);\n+\trevs->previous_parents = copy_commit_list(commit->parents);\n+}\n+\n static struct commit *get_revision_1(struct rev_info *revs)\n {\n \tif (!revs->commits)\n@@ -2935,6 +2966,8 @@ static struct commit *get_revision_1(struct rev_info *revs)\n \t\t\tdie(\"Failed to simplify parents of commit %s\",\n \t\t\t    sha1_to_hex(commit->object.sha1));\n \t\tdefault:\n+\t\t\tif (revs->track_linear)\n+\t\t\t\ttrack_linear(revs, commit);\n \t\t\treturn commit;\n \t\t}\n \t} while (revs->commits);\n@@ -3101,14 +3134,23 @@ struct commit *get_revision(struct rev_info *revs)\n \t\trevs->reverse_output_stage = 1;\n \t}\n \n-\tif (revs->reverse_output_stage)\n-\t\treturn pop_commit(&revs->commits);\n+\tif (revs->reverse_output_stage) {\n+\t\tc = pop_commit(&revs->commits);\n+\t\tif (revs->track_linear)\n+\t\t\trevs->linear = !!(c && c->object.flags & TRACK_LINEAR);\n+\t\treturn c;\n+\t}\n \n \tc = get_revision_internal(revs);\n \tif (c && revs->graph)\n \t\tgraph_update(revs->graph, c);\n-\tif (!c)\n+\tif (!c) {\n \t\tfree_saved_parents(revs);\n+\t\tif (revs->previous_parents) {\n+\t\t\tfree_commit_list(revs->previous_parents);\n+\t\t\trevs->previous_parents = NULL;\n+\t\t}\n+\t}\n \treturn c;\n }\n \ndiff --git a/revision.h b/revision.h\nindex 0262bbd..d9907dd 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -19,7 +19,8 @@\n #define SYMMETRIC_LEFT\t(1u<<8)\n #define PATCHSAME\t(1u<<9)\n #define BOTTOM\t\t(1u<<10)\n-#define ALL_REV_FLAGS\t((1u<<11)-1)\n+#define TRACK_LINEAR\t(1u<<26)\n+#define ALL_REV_FLAGS\t(((1u<<11)-1) | TRACK_LINEAR)\n \n #define DECORATE_SHORT_REFS\t1\n #define DECORATE_FULL_REFS\t2\n@@ -138,6 +139,10 @@ struct rev_info {\n \t\t\tpreserve_subject:1;\n \tunsigned int\tdisable_stdin:1;\n \tunsigned int\tleak_pending:1;\n+\t/* --show-linear-break */\n+\tunsigned int\ttrack_linear:1,\n+\t\t\ttrack_first_time:1,\n+\t\t\tlinear:1;\n \n \tenum date_mode date_mode;\n \n@@ -198,6 +203,9 @@ struct rev_info {\n \n \t/* copies of the parent lists, for --full-diff display */\n \tstruct saved_parents *saved_parents_slab;\n+\n+\tstruct commit_list *previous_parents;\n+\tconst char *break_bar;\n };\n \n extern int ref_excluded(struct string_list *, const char *path);\n-- \n1.9.1.345.ga1a145c\n"},{"id":"237778","messageId":"xmqqr45pkj1o.fsf@gitster.dls.corp.google.com","threadId":"35814","inReplyTo":"1395753807-23228-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v4 2/2] log: add --show-linear-break to help see non-linear history","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-03-25T22:30:43Z","receivedAt":"2014-03-25T22:30:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Option explanation is in rev-list-options.txt. The interaction with -z\n> is left undecided.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  On Fri, Mar 21, 2014 at 2:15 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>  >>  * Get rid of saved_linear, use another flag in struct object instead\n>  >\n>  > I cannot offhand say if I like this change or not.  A flag bit is a\n>  > scarce and limited resource; commit slabs felt more suited for\n>  > implementation of corner case eye-candies.\n>\n>  I leave it in bit 26. We can move it out when we run low on flag bits.\n>\n>  >>  * Fix not showing the break bar after a root commit if the dag graph\n>  >>    has multiple roots\n>  >\n>  > I definitely do not like the way a commit-list data structure is\n>  > abused to hold a phoney element that points at a NULL with its item\n>  > pointer.  Allocate a single bit in revs that says \"I haven't done\n>  > anything yet\" if you want to catch the \"first-ness\" without breaking\n>  > what commit_list_insert() and friends are expecting to see---they\n>  > never expect to see a NULL asked to be on the list, AFAIK.\n>\n>  Fixed.\n>\n>  >>  * Make it work with --graph (although I don't really see the point of\n>  >>    using both at the same time)\n>  >\n>  > I do not see the point, either.  I vaguely recall that the previous\n>  > iteration refused the combination at the option parser level, which\n>  > I think would be the right thing to do.\n>\n>  Fixed.\n\nAll changes look good to me.\n\nEspecially on the last one because the new \"printf()\" calls do not\neven attempt to call into the graph API to tell it that it created\na gutter above or give it a chance to draw all vertical lines to\nconnect the graph part.\n\nWill replace and advance them to \"Will merge to 'next'\" state.\n\nThanks.\n"}]}