{"thread":{"id":"19411","subject":"[TopGit PATCH] tg-graph: print dependency graph like git log --graph","startedAt":"2009-05-19T05:44:35Z","lastAt":"2009-05-20T06:07:05Z","messageCount":18,"participants":["Bert Wesarg","Michael Radziej","martin f krafft","Uwe Kleine-König"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"114229","messageId":"1242711875-25666-1-git-send-email-bert.wesarg@googlemail.com","threadId":"19411","inReplyTo":null,"subject":"[TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2009-05-19T05:44:35Z","receivedAt":"2009-05-19T05:44:35Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"Finally, I have managed to work on an old idea: to display the dependency graph\nline-by-line on the console.  The insiration comes from the git log --graph\ncommand, the implementation too.  I have ported the graph.c file from the\nGIT project into the GVPR graph processing language from the graphviz package.\nThats why I have Cc'ed Adam, the original author of the GIT graph API and I\nwould like to thank him for it.  But I must also apologize to him that I\nhaven't gave him credit in this first patch (only here in the message).  Which\nshould be done, before accepting into the topgit.git.\n\nActually, this is still a proof-of-concept, because I'm uncertain wheather we\nneed a new command for this or not.\n\nAs you can see from the tg graph options, there is many you can tweak.  I think\nthe dfs options gives compacter graphs for depending topics.  Dfs gives\ncompacter graphs for the reversed dependencies (i.e. master as first line) for\nme too.  So dfs should probably the default.\n\nPlease try.\n\nThanks,\nBert\n\nSigned-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\n\n---\n .gitignore                 |    2 +\n Makefile                   |    1 +\n README                     |    6 +\n contrib/tg-completion.bash |   25 +\n share/graph.gvpr           | 1043 ++++++++++++++++++++++++++++++++++++++++++++\n tg-graph.sh                |   74 ++++\n tg-summary.sh              |   47 ++-\n 7 files changed, 1189 insertions(+), 9 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex eb56446..fcc7f95 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -21,6 +21,8 @@\n /tg-import.txt\n /tg-remote\n /tg-remote.txt\n+/tg-graph\n+/tg-graph.txt\n /tg\n .*.swp\n \ndiff --git a/Makefile b/Makefile\nindex 3ce39a5..0de1277 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -38,6 +38,7 @@ install:: all\n \tinstall $(hooks_out) \"$(DESTDIR)$(hooksdir)\"\n \tinstall -d -m 755 \"$(DESTDIR)$(sharedir)\"\n \tinstall -m 644 $(help_out) \"$(DESTDIR)$(sharedir)\"\n+\tinstall -m 644 share/graph.gvpr \"$(DESTDIR)$(sharedir)\"\n \n clean::\n \trm -f tg $(commands_out) $(hooks_out) $(help_out)\ndiff --git a/README b/README\nindex d2f095d..7965d70 100644\n--- a/README\n+++ b/README\n@@ -480,6 +480,12 @@ tg update\n \n \tTODO: tg update -a for updating all topic branches\n \n+tg graph\n+~~~~~~~~\n+\tPrints the dependency graph like git log --graph to the console.\n+\n+\tTODO: fan-in and fan-out graphs\n+\n TODO: tg rename\n \n \ndiff --git a/contrib/tg-completion.bash b/contrib/tg-completion.bash\nindex de8a7b5..5124928 100755\n--- a/contrib/tg-completion.bash\n+++ b/contrib/tg-completion.bash\n@@ -302,6 +302,30 @@ _tg_export ()\n \tesac\n }\n \n+_tg_graph ()\n+{\n+\tlocal cur=\"${COMP_WORDS[COMP_CWORD]}\"\n+\n+\tcase \"$cur\" in\n+\t-*)\n+\t\t__tgcomp \"\n+\t\t\t--color\n+\t\t\t--no-color\n+\t\t\t--bfs\n+\t\t\t--dfs\n+\t\t\t--reverse\n+\t\t\t--no-reverse\n+\t\t\t--header\n+\t\t\t--no-header\n+\t\t\t--body\n+\t\t\t--no-body\n+\t\t\"\n+\t\t;;\n+\t*)\n+\t\t__tgcomp \"$(__tg_refs)\"\n+\tesac\n+}\n+\n _tg_help ()\n {\n \tlocal cur=\"${COMP_WORDS[COMP_CWORD]}\"\n@@ -444,6 +468,7 @@ _tg ()\n \tdelete)      _tg_delete ;;\n \tdepend)      _tg_depend ;;\n \texport)      _tg_export ;;\n+\tgraph)       _tg_graph ;;\n \thelp)        _tg_help ;;\n \timport)      _tg_import ;;\n \tinfo)        _tg_info ;;\ndiff --git a/share/graph.gvpr b/share/graph.gvpr\nnew file mode 100755\nindex 0000000..1a96c22\n--- /dev/null\n+++ b/share/graph.gvpr\n@@ -0,0 +1,1043 @@\n+BEGIN {\n+    int use_stack = 0;\n+    int use_color = 1;\n+    int rev_order = 0;\n+    int show_header = 0;\n+    int show_body = 0;\n+\n+    int argi;\n+    for (argi = 0; argi < ARGC; argi++) {\n+        if      (ARGV[argi] == \"color\")\n+            use_color = 1;\n+        else if (ARGV[argi] == \"no-color\")\n+            use_color = 0;\n+        else if (ARGV[argi] == \"stack\")\n+            use_stack = 1;\n+        else if (ARGV[argi] == \"no-stack\")\n+            use_stack = 0;\n+        else if (ARGV[argi] == \"reverse\")\n+            rev_order = 1;\n+        else if (ARGV[argi] == \"no-reverse\")\n+            rev_order = 0;\n+        else if (ARGV[argi] == \"header\")\n+            show_header = 1;\n+        else if (ARGV[argi] == \"no-header\")\n+            show_header = 0;\n+        else if (ARGV[argi] == \"body\")\n+            show_body = 1;\n+        else if (ARGV[argi] == \"no-body\")\n+            show_body = 0;\n+    }\n+\n+    edge_t first_edge(node_t n)\n+    {\n+        edge_t fe;\n+\n+        if (!rev_order) {\n+            fe = fstout(n);\n+        } else {\n+            fe = fstin(n);\n+        }\n+\n+        return fe;\n+    }\n+\n+    edge_t next_edge(edge_t e)\n+    {\n+        edge_t ne;\n+\n+        if (!rev_order) {\n+            ne = nxtout(e);\n+        } else {\n+            ne = nxtin(e);\n+        }\n+\n+        return ne;\n+    }\n+\n+    node_t adj_node(edge_t e)\n+    {\n+        node_t an;\n+\n+        if (!rev_order) {\n+            an = e.head;\n+        } else {\n+            an = e.tail;\n+        }\n+\n+        return an;\n+    }\n+\n+    int node_deg(node_t n)\n+    {\n+        int nd;\n+\n+        if (!rev_order) {\n+            nd = n.indegree;\n+        } else {\n+            nd = n.outdegree;\n+        }\n+\n+        return nd;\n+    }\n+\n+    int node_num_parents(node_t n)\n+    {\n+        int nnp;\n+\n+        if (!rev_order) {\n+            nnp = n.outdegree;\n+        } else {\n+            nnp = n.indegree;\n+        }\n+\n+        return nnp;\n+    }\n+\n+    /* enum graph_state */\n+    int GRAPH_PADDING    = 0;\n+    int GRAPH_SKIP       = 1;\n+    int GRAPH_PRE_COMMIT = 2;\n+    int GRAPH_COMMIT     = 3;\n+    int GRAPH_POST_MERGE = 4;\n+    int GRAPH_COLLAPSING = 5;\n+\n+    /*\n+     * The list of available column colors.\n+     */\n+    string GIT_COLOR_NORMAL     = \"\";\n+    string GIT_COLOR_RESET      = \"\\033[m\";\n+    string GIT_COLOR_BOLD       = \"\\033[1m\";\n+    string GIT_COLOR_RED        = \"\\033[31m\";\n+    string GIT_COLOR_GREEN      = \"\\033[32m\";\n+    string GIT_COLOR_YELLOW     = \"\\033[33m\";\n+    string GIT_COLOR_BLUE       = \"\\033[34m\";\n+    string GIT_COLOR_MAGENTA    = \"\\033[35m\";\n+    string GIT_COLOR_CYAN       = \"\\033[36m\";\n+    string GIT_COLOR_BG_RED     = \"\\033[41m\";\n+\n+    string gg_column_colors[];\n+    gg_column_colors[ 0] = GIT_COLOR_RED;\n+    gg_column_colors[ 1] = GIT_COLOR_GREEN;\n+    gg_column_colors[ 2] = GIT_COLOR_YELLOW;\n+    gg_column_colors[ 3] = GIT_COLOR_BLUE;\n+    gg_column_colors[ 4] = GIT_COLOR_MAGENTA;\n+    gg_column_colors[ 5] = GIT_COLOR_CYAN;\n+    gg_column_colors[ 6] = GIT_COLOR_BOLD + GIT_COLOR_RED;\n+    gg_column_colors[ 7] = GIT_COLOR_BOLD + GIT_COLOR_GREEN;\n+    gg_column_colors[ 8] = GIT_COLOR_BOLD + GIT_COLOR_YELLOW;\n+    gg_column_colors[ 9] = GIT_COLOR_BOLD + GIT_COLOR_BLUE;\n+    gg_column_colors[10] = GIT_COLOR_BOLD + GIT_COLOR_MAGENTA;\n+    gg_column_colors[11] = GIT_COLOR_BOLD + GIT_COLOR_CYAN;\n+    int COLUMN_COLORS_MAX = 12;\n+\n+    /*\n+     * The node currently being processed\n+     */\n+    node_t gg_n;\n+    /*\n+     * The number of interesting parents that this commit has.\n+     */\n+    int gg_num_parents;\n+    /*\n+     * The width of the graph output for this commit.\n+     * All rows for this commit are padded to this width, so that\n+     * messages printed after the graph output are aligned.\n+     */\n+    int gg_width;\n+    /*\n+     * The next expansion row to print\n+     * when state is GRAPH_PRE_COMMIT\n+     */\n+    int gg_expansion_row;\n+    /*\n+     * The current output state.\n+     * This tells us what kind of line graph_next_line() should output.\n+     */\n+    int gg_state;\n+    /*\n+     * The output state for the previous line of output.\n+     * This is primarily used to determine how the first merge line\n+     * should appear, based on the last line of the previous commit.\n+     */\n+    int gg_prev_state;\n+    /*\n+     * The index of the column that refers to this commit.\n+     *\n+     * If none of the incoming columns refer to this commit,\n+     * this will be equal to num_columns.\n+     */\n+    int gg_n_index;\n+    /*\n+     * The commit_index for the previously displayed commit.\n+     *\n+     * This is used to determine how the first line of a merge\n+     * graph output should appear, based on the last line of the\n+     * previous commit.\n+     */\n+    int gg_prev_n_index;\n+    /*\n+     * The number of columns (also called \"branch lines\" in some places)\n+     */\n+    int gg_num_columns;\n+    /*\n+     * The number of columns in the new_columns array\n+     */\n+    int gg_num_new_columns;\n+    /*\n+     * The number of entries in the mapping array\n+     */\n+    int gg_mapping_size;\n+    /*\n+     * The column state before we output the current commit.\n+     */\n+    node_t gg_columns[];\n+    /*\n+     * The new column state after we output the current commit.\n+     * Only valid when state is GRAPH_COLLAPSING.\n+     */\n+    node_t gg_new_columns[];\n+    /*\n+     * An array that tracks the current state of each\n+     * character in the output line during state GRAPH_COLLAPSING.\n+     * Each entry is -1 if this character is empty, or a non-negative\n+     * integer if the character contains a branch line.  The value of\n+     * the integer indicates the target position for this branch line.\n+     * (I.e., this array maps the current column positions to their\n+     * desired positions.)\n+     *\n+     * The maximum capacity of this array is always\n+     * sizeof(int) * 2 * column_capacity.\n+     */\n+    int gg_mapping[];\n+    /*\n+     * A temporary array for computing the next mapping state\n+     * while we are outputting a mapping line.  This is stored as part\n+     * of the git_graph simply so we dont have to allocate a new\n+     * temporary array each time we have to output a collapsing line.\n+     */\n+    int gg_new_mapping[];\n+    /*\n+     * The current default column color being used.  This is\n+     * stored as an index into the array column_colors.\n+     */\n+    unsigned gg_default_column_color;\n+\n+    string gg_write_column(node_t n, string col_char)\n+    {\n+        return n.col_color + col_char + n.col_color_end;\n+    }\n+\n+    void graph_set_current_column_color(node_t n)\n+    {\n+        if (!use_color) {\n+            n.col_color     = \"\";\n+            n.col_color_end = \"\";\n+        } else {\n+            n.col_color     = gg_column_colors[gg_default_column_color];\n+            n.col_color_end = GIT_COLOR_RESET;\n+        }\n+    }\n+\n+    /*\n+     * Update the graph's default column color.\n+     */\n+    void graph_increment_column_color(void)\n+    {\n+        gg_default_column_color = (gg_default_column_color + 1) %\n+                COLUMN_COLORS_MAX;\n+    }\n+\n+    void graph_set_commit_color(node_t n)\n+    {\n+        if (n.need_col_color == \"yes\")\n+            graph_set_current_column_color(n);\n+        n.need_col_color = \"no\";\n+    }\n+\n+    int graph_insert_into_new_columns(node_t gg_iinc_n, int gg_iinc_mapping_index)\n+    {\n+        int gg_iinc_i;\n+\n+        /*\n+         * If the commit is already in the new_columns list, we do not need to\n+         * add it.  Just update the mapping correctly.\n+         */\n+        for (gg_iinc_i = 0; gg_iinc_i < gg_num_new_columns; gg_iinc_i++) {\n+            if (gg_new_columns[gg_iinc_i] == gg_iinc_n) {\n+                gg_mapping[gg_iinc_mapping_index] = gg_iinc_i;\n+                return gg_iinc_mapping_index + 2;\n+            }\n+        }\n+\n+        /*\n+         * This commit is not already in new_columns.  Add it.\n+         */\n+        gg_new_columns[gg_num_new_columns] = gg_iinc_n;\n+        graph_set_commit_color(gg_iinc_n);\n+        gg_mapping[gg_iinc_mapping_index] = gg_num_new_columns;\n+        gg_num_new_columns++;\n+\n+        return gg_iinc_mapping_index + 2;\n+    }\n+\n+    void graph_update_width(int is_commit_in_existing_columns)\n+    {\n+        /*\n+         * Compute the width needed to display the graph for this commit.\n+         * This is the maximum width needed for any row.  All other rows\n+         * will be padded to this width.\n+         *\n+         * Compute the number of columns in the widest row:\n+         * Count each existing column (gg_num_columns), and each new\n+         * column added by this commit.\n+         */\n+        int gg_uw_max_cols = gg_num_columns + gg_num_parents;\n+\n+        /*\n+         * Even if the current commit has no parents to be printed, it\n+         * still takes up a column for itself.\n+         */\n+        if (gg_num_parents < 1)\n+            gg_uw_max_cols++;\n+\n+        /*\n+         * We added a column for the the current commit as part of\n+         * gg_num_parents.  If the current commit was already in\n+         * gg_columns, then we have double counted it.\n+         */\n+        if (is_commit_in_existing_columns)\n+            gg_uw_max_cols--;\n+\n+        /*\n+         * Each column takes up 2 spaces\n+         */\n+        gg_width = gg_uw_max_cols * 2;\n+    }\n+\n+    void graph_update_columns(void)\n+    {\n+        edge_t gg_uc_e;\n+        int gg_uc_mapping_idx;\n+        int gg_uc_i, gg_uc_seen_this, is_commit_in_columns;\n+\n+        /*\n+         * Swap gg_columns with gg_new_columns\n+         * gg_columns contains the state for the previous commit,\n+         * and new_columns now contains the state for our commit.\n+         */\n+        for (gg_uc_i = 0; gg_uc_i < gg_num_new_columns; gg_uc_i++)\n+            gg_columns[gg_uc_i] = gg_new_columns[gg_uc_i];\n+        gg_num_columns = gg_num_new_columns;\n+        gg_num_new_columns = 0;\n+\n+        /*\n+         * Clear out gg_mapping\n+         */\n+        gg_mapping_size = 2 * (gg_num_columns + gg_num_parents);\n+        for (gg_uc_i = 0; gg_uc_i < gg_mapping_size; gg_uc_i++)\n+            gg_mapping[gg_uc_i] = -1;\n+\n+        /*\n+         * Populate gg_new_columns and gg_mapping\n+         *\n+         * Some of the parents of this commit may already be in\n+         * gg_columns.  If so, gg_new_columns should only contain a\n+         * single entry for each such commit.  gg_mapping should\n+         * contain information about where each current branch line is\n+         * supposed to end up after the collapsing is performed.\n+         */\n+        gg_uc_seen_this = 0;\n+        gg_uc_mapping_idx = 0;\n+        is_commit_in_columns = 1;\n+        for (gg_uc_i = 0; gg_uc_i <= gg_num_columns; gg_uc_i++) {\n+            node_t gg_uc_col_n;\n+            if (gg_uc_i == gg_num_columns) {\n+                if (gg_uc_seen_this)\n+                    break;\n+                is_commit_in_columns = 0;\n+                gg_uc_col_n = gg_n;\n+            } else {\n+                gg_uc_col_n = gg_columns[gg_uc_i];\n+            }\n+\n+            if (gg_uc_col_n == gg_n) {\n+                int old_mapping_idx = gg_uc_mapping_idx;\n+                gg_uc_seen_this = 1;\n+                gg_n_index = gg_uc_i;\n+                for (gg_uc_e = first_edge(gg_n); gg_uc_e; gg_uc_e = next_edge(gg_uc_e)) {\n+                    /*\n+                     * If this is a merge increment the current\n+                     * color.\n+                     */\n+                    if (gg_num_parents > 1)\n+                             graph_increment_column_color();\n+                    \n+                    gg_uc_mapping_idx = graph_insert_into_new_columns(\n+                            adj_node(gg_uc_e), gg_uc_mapping_idx);\n+                }\n+                if (gg_uc_mapping_idx == old_mapping_idx)\n+                    gg_uc_mapping_idx += 2;\n+            } else {\n+                gg_uc_mapping_idx = graph_insert_into_new_columns(gg_uc_col_n,\n+                        gg_uc_mapping_idx);\n+            }\n+        }\n+\n+        /*\n+         * Shrink mapping_size to be the minimum necessary\n+         */\n+        while (gg_mapping_size > 1 &&\n+               gg_mapping[gg_mapping_size - 1] < 0)\n+            gg_mapping_size--;\n+\n+        /*\n+         * Compute gg_width for this commit\n+         */\n+        graph_update_width(is_commit_in_columns);\n+    }\n+\n+    void graph_update_state(int gg_us_s)\n+    {\n+        gg_prev_state = gg_state;\n+        gg_state = gg_us_s;\n+    }\n+\n+    void graph_update(node_t n) {\n+        gg_n = n;\n+        gg_num_parents = node_num_parents(n);\n+        gg_prev_n_index = gg_n_index;\n+        graph_update_columns();\n+        gg_expansion_row = 0;\n+        if (gg_state != GRAPH_PADDING)\n+            gg_state = GRAPH_SKIP;\n+        else if (gg_num_parents >= 3 &&\n+                 gg_n_index < (gg_num_columns - 1))\n+            gg_state = GRAPH_PRE_COMMIT;\n+        else\n+            gg_state = GRAPH_COMMIT;\n+    }\n+\n+    string graph_pad_horizontally(int chars_written)\n+    {\n+        int gg_ph_extra, gg_ph_i;\n+        string gg_ph_sb = \"\";\n+\n+        if (chars_written >= gg_width)\n+            return gg_ph_sb;\n+\n+        gg_ph_extra = gg_width - chars_written;\n+        for (gg_ph_i = 0; gg_ph_i < gg_ph_extra; gg_ph_i++)\n+            gg_ph_sb += \" \";\n+\n+        return gg_ph_sb;\n+    }\n+\n+    string graph_output_padding_line(void)\n+    {\n+        int gg_opl_i;\n+        string gg_opl_sb = \"\";\n+\n+        for (gg_opl_i = 0; gg_opl_i < gg_num_new_columns; gg_opl_i++) {\n+            gg_opl_sb += gg_write_column(gg_new_columns[gg_opl_i], \"|\");\n+            gg_opl_sb += \" \";\n+        }\n+\n+        gg_opl_sb += graph_pad_horizontally(gg_num_new_columns * 2);\n+\n+        return gg_opl_sb;\n+    }\n+\n+    string graph_output_skip_line(void)\n+    {\n+        string gg_osl_sb = \"\";\n+        /*\n+         * Output an ellipsis to indicate that a portion\n+         * of the graph is missing.\n+         */\n+        gg_osl_sb += \"...\";\n+        gg_osl_sb += graph_pad_horizontally(3);\n+\n+        if (gg_num_parents >= 3 && gg_n_index < (gg_num_columns - 1))\n+            graph_update_state(GRAPH_PRE_COMMIT);\n+        else\n+            graph_update_state(GRAPH_COMMIT);\n+\n+        return gg_osl_sb;\n+    }\n+\n+    string graph_output_pre_commit_line(void)\n+    {\n+        int gg_opcl_num_expansion_rows;\n+        int gg_opcl_i, gg_opcl_j, gg_opcl_seen_this;\n+        int gg_opcl_chars_written;\n+        string gg_opcl_sb = \"\";\n+\n+        /*\n+         * This function formats a row that increases the space around a commit\n+         * with multiple parents, to make room for it.  It should only be\n+         * called when there are 3 or more parents.\n+         *\n+         * We need 2 extra rows for every parent over 2.\n+         */\n+        if (!(gg_num_parents >= 3))\n+            exit(1);\n+        gg_opcl_num_expansion_rows = (gg_num_parents - 2) * 2;\n+\n+        /*\n+         * gg_expansion_row tracks the current expansion row we are on.\n+         * It should be in the range [0, gg_opcl_num_expansion_rows - 1]\n+         */\n+        if (!(0 <= gg_expansion_row &&\n+               gg_expansion_row < gg_opcl_num_expansion_rows))\n+            exit(1);\n+\n+        /*\n+         * Output the row\n+         */\n+        gg_opcl_seen_this = 0;\n+        gg_opcl_chars_written = 0;\n+        for (gg_opcl_i = 0; gg_opcl_i < gg_num_columns; gg_opcl_i++) {\n+            if (gg_columns[gg_opcl_i] == gg_n) {\n+                gg_opcl_seen_this = 1;\n+                gg_opcl_sb += gg_write_column(gg_columns[gg_opcl_i], \"|\");\n+                for (gg_opcl_j = 0; gg_opcl_j < gg_expansion_row; gg_opcl_j++)\n+                    gg_opcl_sb += \" \";\n+                gg_opcl_chars_written += 1 + gg_expansion_row;\n+            } else if (gg_opcl_seen_this && (gg_expansion_row == 0)) {\n+                /*\n+                 * This is the first line of the pre-commit output.\n+                 * If the previous commit was a merge commit and\n+                 * ended in the GRAPH_POST_MERGE state, all branch\n+                 * lines after gg_prev_commit_index were\n+                 * printed as \"\\\" on the previous line.  Continue\n+                 * to print them as \"\\\" on this line.  Otherwise,\n+                 * print the branch lines as \"|\".\n+                 */\n+                if (gg_prev_state == GRAPH_POST_MERGE &&\n+                    gg_prev_commit_index < gg_opcl_i)\n+                    gg_opcl_sb += gg_write_column(gg_columns[gg_opcl_i], \"\\\\\");\n+                else\n+                    gg_opcl_sb += gg_write_column(gg_columns[gg_opcl_i], \"|\");\n+                gg_opcl_chars_written++;\n+            } else if (gg_opcl_seen_this && (gg_expansion_row > 0)) {\n+                gg_opcl_sb += gg_write_column(gg_columns[gg_opcl_i], \"\\\\\");\n+                gg_opcl_chars_written++;\n+            } else {\n+                gg_opcl_sb += gg_write_column(gg_columns[gg_opcl_i], \"|\");\n+                gg_opcl_chars_written++;\n+            }\n+            gg_opcl_sb += \" \";\n+            gg_opcl_chars_written++;\n+        }\n+\n+        gg_opcl_sb += graph_pad_horizontally(gg_opcl_chars_written);\n+\n+        /*\n+         * Increment gg_expansion_row,\n+         * and move to state GRAPH_COMMIT if necessary\n+         */\n+        gg_expansion_row++;\n+        if (gg_expansion_row >= gg_opcl_num_expansion_rows)\n+            graph_update_state(GRAPH_COMMIT);\n+\n+        return gg_opcl_sb;\n+    }\n+\n+    /*\n+     * Here dashless_commits represents the number of parents\n+     * which do not need to have dashes (because their edges fit\n+     * neatly under the commit).\n+     */\n+    int gg_dom_dashless_commits = 2;\n+    int graph_draw_octopus_merge_width(void)\n+    {\n+        return ((gg_num_parents - gg_dom_dashless_commits) * 2);\n+    }\n+\n+    string graph_draw_octopus_merge(void)\n+    {\n+        int gg_dom_i;\n+        int gg_dom_col_num;\n+        string gg_dom_sb = \"\";\n+        int gg_dom_num_dashes = graph_draw_octopus_merge_width() - 1;\n+\n+        for (gg_dom_i = 0; gg_dom_i < gg_dom_num_dashes; gg_dom_i++) {\n+            gg_dom_col_num = (gg_dom_i / 2) + gg_dom_dashless_commits;\n+            gg_dom_sb += gg_write_column(gg_new_columns[gg_dom_col_num], \"-\");\n+        }\n+        gg_dom_col_num = (gg_dom_i / 2) + gg_dom_dashless_commits;\n+        gg_dom_sb += gg_write_column(gg_new_columns[gg_dom_col_num], \".\");\n+\n+        return gg_dom_sb;\n+    }\n+\n+    int graph_is_mapping_correct(void)\n+    {\n+        int gg_imc_i;\n+\n+        /*\n+         * The mapping is up to date if each entry is at its target,\n+         * or is 1 greater than its target.\n+         * (If it is 1 greater than the target, '/' will be printed, so it\n+         * will look correct on the next row.)\n+         */\n+        for (gg_imc_i = 0; gg_imc_i < gg_mapping_size; gg_imc_i++) {\n+            int gg_imc_target = gg_mapping[gg_imc_i];\n+            if (gg_imc_target < 0)\n+                continue;\n+            if (gg_imc_target == (gg_imc_i / 2))\n+                continue;\n+            return 0;\n+        }\n+\n+        return 1;\n+    }\n+\n+    string graph_output_commit_line(void)\n+    {\n+        int gg_ocl_seen_this = 0;\n+        int gg_ocl_i, gg_ocl_chars_written;\n+        string gg_ocl_sb = \"\";\n+\n+        /*\n+         * Output the row containing this commit\n+         * Iterate up to and including gg_num_columns,\n+         * since the current commit may not be in any of the existing\n+         * columns.  (This happens when the current commit does not have any\n+         * children that we have already processed.)\n+         */\n+        gg_ocl_seen_this = 0;\n+        gg_ocl_chars_written = 0;\n+        for (gg_ocl_i = 0; gg_ocl_i <= gg_num_columns; gg_ocl_i++) {\n+            node_t gg_ocl_n;\n+            if (gg_ocl_i == gg_num_columns) {\n+                if (gg_ocl_seen_this)\n+                    break;\n+                gg_ocl_n = gg_n;\n+            } else {\n+                gg_ocl_n = gg_columns[gg_ocl_i];\n+            }\n+\n+            if (gg_ocl_n == gg_n) {\n+                gg_ocl_seen_this = 1;\n+                gg_ocl_sb += \"*\";\n+                gg_ocl_chars_written++;\n+\n+                if (gg_num_parents > 2) {\n+                    gg_ocl_sb            += graph_draw_octopus_merge();\n+                    gg_ocl_chars_written += graph_draw_octopus_merge_width();\n+                }\n+            } else if (gg_ocl_seen_this && (gg_num_parents > 2)) {\n+                gg_ocl_sb += gg_write_column(gg_columns[gg_ocl_i], \"\\\\\");\n+                gg_ocl_chars_written++;\n+            } else if (gg_ocl_seen_this && (gg_num_parents == 2)) {\n+                /*\n+                 * This is a 2-way merge commit.\n+                 * There is no GRAPH_PRE_COMMIT stage for 2-way\n+                 * merges, so this is the first line of output\n+                 * for this commit.  Check to see what the previous\n+                 * line of output was.\n+                 */\n+                if (gg_prev_state == GRAPH_POST_MERGE &&\n+                    gg_prev_n_index < gg_ocl_i)\n+                    gg_ocl_sb += gg_write_column(gg_columns[gg_ocl_i], \"\\\\\");\n+                else\n+                    gg_ocl_sb += gg_write_column(gg_columns[gg_ocl_i], \"|\");\n+                gg_ocl_chars_written++;\n+            } else {\n+                gg_ocl_sb += gg_write_column(gg_columns[gg_ocl_i], \"|\");\n+                gg_ocl_chars_written++;\n+            }\n+            gg_ocl_sb += \" \";\n+            gg_ocl_chars_written++;\n+        }\n+\n+        gg_ocl_sb += graph_pad_horizontally(gg_ocl_chars_written);\n+\n+        /*\n+         * Update gg_state\n+         */\n+        if (gg_num_parents > 1)\n+            graph_update_state(GRAPH_POST_MERGE);\n+        else if (graph_is_mapping_correct())\n+            graph_update_state(GRAPH_PADDING);\n+        else\n+            graph_update_state(GRAPH_COLLAPSING);\n+\n+        return gg_ocl_sb;\n+    }\n+\n+    node_t find_new_column_by_commit(node_t n)\n+    {\n+        int gg_fnc_i;\n+\n+        for (gg_fnc_i = 0; gg_fnc_i < gg_num_new_columns; gg_fnc_i++) {\n+            if (gg_new_columns[gg_fnc_i] == n)\n+                return gg_new_columns[gg_fnc_i];\n+        }\n+\n+        return NULL;\n+    }\n+\n+    string graph_output_post_merge_line(void)\n+    {\n+        int gg_opml_seen_this = 0;\n+        int gg_opml_i, gg_opml_j, gg_opml_chars_written;\n+        string gg_opml_sb = \"\";\n+\n+        /*\n+         * Output the post-merge row\n+         */\n+        gg_opml_chars_written = 0;\n+        for (gg_opml_i = 0; gg_opml_i <= gg_num_columns; gg_opml_i++) {\n+            node_t gg_opml_col_n;\n+            if (gg_opml_i == gg_num_columns) {\n+                if (gg_opml_seen_this)\n+                    break;\n+                gg_opml_col_n = gg_n;\n+            } else {\n+                gg_opml_col_n = gg_columns[gg_opml_i];\n+            }\n+\n+            if (gg_opml_col_n == gg_n) {\n+                edge_t gg_opml_edge;\n+                node_t gg_opml_par_column;\n+                /*\n+                 * Since the current commit is a merge find\n+                 * the columns for the parent commits in\n+                 * new_columns and use those to format the\n+                 * edges.\n+                 */\n+                gg_opml_seen_this = 1;\n+\n+                gg_opml_edge = first_edge(gg_n);\n+                gg_opml_par_column = adj_node(gg_opml_edge);\n+\n+                gg_opml_sb += gg_write_column(gg_opml_par_column, \"|\");\n+                gg_opml_chars_written++;\n+                for (gg_opml_j = 0; gg_opml_j < gg_num_parents - 1; gg_opml_j++) {\n+                    gg_opml_edge = next_edge(gg_opml_edge);\n+                    gg_opml_par_column = adj_node(gg_opml_edge);\n+                    gg_opml_sb += gg_write_column(gg_opml_par_column, \"\\\\\");\n+                    gg_opml_sb += \" \";\n+                }\n+                gg_opml_chars_written += gg_opml_j * 2;\n+\n+            } else if (gg_opml_seen_this) {\n+                gg_opml_sb += gg_write_column(gg_columns[gg_opml_i], \"\\\\\");\n+                gg_opml_sb += \" \";\n+                gg_opml_chars_written += 2;\n+\n+            } else {\n+                gg_opml_sb += gg_write_column(gg_columns[gg_opml_i], \"|\");\n+                gg_opml_sb += \" \";\n+                gg_opml_chars_written += 2;\n+            }\n+        }\n+\n+        gg_opml_sb += graph_pad_horizontally(gg_opml_chars_written);\n+\n+        /*\n+         * Update gg_state\n+         */\n+        if (graph_is_mapping_correct())\n+            graph_update_state(GRAPH_PADDING);\n+        else\n+            graph_update_state(GRAPH_COLLAPSING);\n+\n+        return gg_opml_sb;\n+    }\n+\n+    string graph_output_collapsing_line(void)\n+    {\n+        int gg_oCl_i;\n+        int gg_oCl_target;\n+        int gg_oCl_used_horizontal = 0;\n+        int gg_oCl_horizontal_edge = -1;\n+        int gg_oCl_horizontal_edge_target = -1;\n+        string gg_oCl_sb = \"\";\n+ \n+        /*\n+         * Clear out the new_mapping array\n+         */\n+        for (gg_oCl_i = 0; gg_oCl_i < gg_mapping_size; gg_oCl_i++)\n+            gg_new_mapping[gg_oCl_i] = -1;\n+\n+        for (gg_oCl_i = 0; gg_oCl_i < gg_mapping_size; gg_oCl_i++) {\n+            gg_oCl_target = gg_mapping[gg_oCl_i];\n+            if (gg_oCl_target < 0)\n+                continue;\n+\n+            /*\n+             * Since update_columns() always inserts the leftmost\n+             * column first, each branch's target location should\n+             * always be either its current location or to the left of\n+             * its current location.\n+             *\n+             * We never have to move branches to the right.  This makes\n+             * the graph much more legible, since whenever branches\n+             * cross, only one is moving directions.\n+             */\n+            if (!(gg_oCl_target * 2 <= gg_oCl_i))\n+                exit(1);\n+\n+            if (gg_oCl_target * 2 == gg_oCl_i) {\n+                /*\n+                 * This column is already in the\n+                 * correct place\n+                 */\n+                if (!(gg_new_mapping[gg_oCl_i] == -1))\n+                    exit(1);\n+                gg_new_mapping[gg_oCl_i] = gg_oCl_target;\n+            } else if (gg_new_mapping[gg_oCl_i - 1] < 0) {\n+                /*\n+                 * Nothing is to the left.\n+                 * Move to the left by one\n+                 */\n+                gg_new_mapping[gg_oCl_i - 1] = gg_oCl_target;\n+                /*\n+                 * If there isn't already an edge moving horizontally\n+                 * select this one.\n+                 */\n+                if (gg_oCl_horizontal_edge == -1) {\n+                        int gg_oCl_j;\n+                        gg_oCl_horizontal_edge = gg_oCl_i;\n+                        gg_oCl_horizontal_edge_target = gg_oCl_target;\n+                        /*\n+                         * The variable target is the index of the graph\n+                         * column, and therefore target*2+3 is the\n+                         * actual screen column of the first horizontal\n+                         * line.\n+                         */\n+                        for (gg_oCl_j = (gg_oCl_target * 2) + 3;\n+                             gg_oCl_j < (gg_oCl_i - 2);\n+                             gg_oCl_j += 2)\n+                                gg_new_mapping[gg_oCl_j] = gg_oCl_target;\n+                }\n+            } else if (gg_new_mapping[gg_oCl_i - 1] == gg_oCl_target) {\n+                /*\n+                 * There is a branch line to our left\n+                 * already, and it is our target.  We\n+                 * combine with this line, since we share\n+                 * the same parent commit.\n+                 *\n+                 * We don't have to add anything to the\n+                 * output or new_mapping, since the\n+                 * existing branch line has already taken\n+                 * care of it.\n+                 */\n+            } else {\n+                /*\n+                 * There is a branch line to our left,\n+                 * but it isn't our target.  We need to\n+                 * cross over it.\n+                 *\n+                 * The space just to the left of this\n+                 * branch should always be empty.\n+                 *\n+                 * The branch to the left of that space\n+                 * should be our eventual target.\n+                 */\n+                if (!(gg_new_mapping[gg_oCl_i - 1] > gg_oCl_target))\n+                    exit(1);\n+                if (!(gg_new_mapping[gg_oCl_i - 2] < 0))\n+                    exit(1);\n+                if (!(gg_new_mapping[gg_oCl_i - 3] == gg_oCl_target))\n+                    exit(1);\n+                gg_new_mapping[gg_oCl_i - 2] = gg_oCl_target;\n+                /*\n+                 * Mark this branch as the horizontal edge to\n+                 * prevent any other edges from moving\n+                 * horizontally.\n+                 */\n+                if (gg_oCl_horizontal_edge == -1)\n+                    gg_oCl_horizontal_edge = gg_oCl_i;\n+            }\n+        }\n+\n+        /*\n+         * The new mapping may be 1 smaller than the old mapping\n+         */\n+        if (gg_new_mapping[gg_mapping_size - 1] < 0)\n+            gg_mapping_size--;\n+\n+        /*\n+         * Output out a line based on the new mapping info\n+         */\n+        for (gg_oCl_i = 0; gg_oCl_i < gg_mapping_size; gg_oCl_i++) {\n+            gg_oCl_target = gg_new_mapping[gg_oCl_i];\n+            if (gg_oCl_target < 0)\n+                gg_oCl_sb += \" \";\n+            else if (gg_oCl_target * 2 == gg_oCl_i)\n+                gg_oCl_sb += gg_write_column(gg_new_columns[gg_oCl_target], \"|\");\n+            else if (gg_oCl_target == gg_oCl_horizontal_edge_target &&\n+                     gg_oCl_i != gg_oCl_horizontal_edge - 1) {\n+                /*\n+                 * Set the mappings for all but the\n+                 * first segment to -1 so that they\n+                 * won't continue into the next line.\n+                 */\n+                if (gg_oCl_i != (gg_oCl_target * 2) + 3)\n+                        gg_new_mapping[gg_oCl_i] = -1;\n+                gg_oCl_used_horizontal = 1;\n+                gg_oCl_sb += gg_write_column(gg_new_columns[gg_oCl_target], \"_\");\n+            } else {\n+                if (gg_oCl_used_horizontal && gg_oCl_i < gg_oCl_horizontal_edge)\n+                        gg_new_mapping[gg_oCl_i] = -1;\n+                gg_oCl_sb += gg_write_column(gg_new_columns[gg_oCl_target], \"/\");\n+            }\n+        }\n+\n+        gg_oCl_sb += graph_pad_horizontally(gg_mapping_size);\n+\n+        /*\n+         * Swap mapping and new_mapping\n+         */\n+        for (gg_oCl_i = 0; gg_oCl_i < gg_mapping_size; gg_oCl_i++) {\n+            int gg_oCl_tmp = gg_mapping[gg_oCl_i];\n+            gg_mapping[gg_oCl_i] = gg_new_mapping[gg_oCl_i];\n+            gg_new_mapping[gg_oCl_i] = gg_oCl_tmp;\n+        }\n+\n+        /*\n+         * If gg_mapping indicates that all of the branch lines\n+         * are already in the correct positions, we are done.\n+         * Otherwise, we need to collapse some branch lines together.\n+         */\n+        if (graph_is_mapping_correct())\n+            graph_update_state(GRAPH_PADDING);\n+\n+        return gg_oCl_sb;\n+    }\n+\n+    int graph_next_line_is_commit(void)\n+    {\n+        int gg_nlic_ret = 0;\n+\n+        if (gg_state == GRAPH_COMMIT) {\n+            gg_nlic_ret = 1;\n+        }\n+\n+        return gg_nlic_ret;\n+    }\n+\n+    string graph_next_line(void)\n+    {\n+        string gg_nl_ret = \"\";\n+\n+        if        (gg_state == GRAPH_PADDING) {\n+            gg_nl_ret += graph_output_padding_line();\n+        } else if (gg_state == GRAPH_SKIP) {\n+            gg_nl_ret += graph_output_skip_line();\n+        } else if (gg_state == GRAPH_PRE_COMMIT) {\n+            gg_nl_ret += graph_output_pre_commit_line();\n+        } else if (gg_state == GRAPH_COMMIT) {\n+            gg_nl_ret += graph_output_commit_line();\n+        } else if (gg_state == GRAPH_POST_MERGE) {\n+            gg_nl_ret += graph_output_post_merge_line();\n+        } else if (gg_state == GRAPH_COLLAPSING) {\n+            gg_nl_ret += graph_output_collapsing_line();\n+        }\n+\n+        return gg_nl_ret;\n+    }\n+\n+}\n+BEG_G {\n+    node_t ts_empty_deg[];\n+    int ts_head = 0, ts_tail = 0;\n+    int ts_nedges = 0;\n+    $tvtype = TV_ne;\n+\n+    gg_n = NULL;\n+    gg_num_parents = 0;\n+    gg_width = 0;\n+    gg_expansion_row = 0;\n+    gg_state = GRAPH_PADDING;\n+    gg_prev_state = GRAPH_PADDING;\n+    gg_n_index = 0;\n+    gg_prev_n_index = 0;\n+    gg_num_columns = 0;\n+    gg_num_new_columns = 0;\n+    gg_mapping_size = 0;\n+    gg_default_column_color = 0;\n+}\n+N {\n+    $.ts_deg = node_deg($);\n+    if ($.ts_deg == 0)  {\n+        ts_empty_deg[ts_tail] = $;\n+        ts_tail++;\n+    }\n+    $.need_col_color = \"yes\";\n+}\n+E {\n+    $.ts_deleted = \"false\";\n+}\n+END_G {\n+    while (use_stack ? ts_tail > 0 : ts_head < ts_tail) {\n+        node_t n;\n+        edge_t e;\n+        int i;\n+        int lines;\n+\n+        if (use_stack) {\n+            n = ts_empty_deg[--ts_tail];\n+        } else {\n+            n = ts_empty_deg[ts_head];\n+            ts_head++;\n+        }\n+\n+        graph_update(n);\n+\n+        /* print lines till commit line */\n+        while (gg_state != GRAPH_PADDING) {\n+            if (graph_next_line_is_commit())\n+                break;\n+            printf(\"%s\\n\", graph_next_line());\n+        }\n+\n+        /* print 'commit message'  */\n+        if (use_color) {\n+            printf(\"%s%s%s%s\\n\", graph_next_line(),\n+                                 GIT_COLOR_YELLOW, n.name, GIT_COLOR_RESET);\n+        } else {\n+            printf(\"%s%s\\n\", graph_next_line(), n.name);\n+        }\n+\n+        if (show_header && aget(n, \"tg_header_lines\")) {\n+            lines = aget(n, \"tg_header_lines\");\n+            for (i = 0; i < lines; i++) {\n+                printf(\"%s%s\\n\", graph_next_line(), aget(n, sprintf(\"tg_header_%d\", i)));\n+            }\n+            printf(\"%s\\n\", graph_next_line());\n+        }\n+\n+        if (show_body && aget(n, \"tg_body_lines\")) {\n+            lines = aget(n, \"tg_body_lines\");\n+            for (i = 0; i < lines; i++) {\n+                printf(\"%s    %s\\n\", graph_next_line(), aget(n, sprintf(\"tg_body_%d\", i)));\n+            }\n+            printf(\"%s\\n\", graph_next_line());\n+        }\n+\n+        /* print remaing graph lines */\n+        while (gg_state != GRAPH_PADDING) {\n+            printf(\"%s\\n\", graph_next_line());\n+        }\n+\n+        for (e = first_edge(n); e; e = next_edge(e)) {\n+            if (e.ts_deleted == \"false\") {\n+                node_t d = adj_node(e);\n+                d.ts_deg = (int)d.ts_deg - 1;\n+\n+                if (d.ts_deg == 0)  {\n+                    ts_empty_deg[ts_tail] = d;\n+                    ts_tail++;\n+                }\n+\n+                e.ts_deleted = \"true\";\n+            }\n+        }\n+    }\n+}\ndiff --git a/tg-graph.sh b/tg-graph.sh\nnew file mode 100755\nindex 0000000..f997b28\n--- /dev/null\n+++ b/tg-graph.sh\n@@ -0,0 +1,74 @@\n+#!/bin/sh\n+\n+color=\n+do_dfs=false\n+do_bfs=false\n+stack=\n+reverse=\n+header=\n+body=\n+\n+summary_graphviz=--graphviz\n+\n+name=\n+\n+while [ -n \"$1\" ]; do\n+\targ=\"$1\"; shift\n+\tcase \"$arg\" in\n+\t--color)\n+\t\tcolor=\"color\";;\n+\t--no-color)\n+\t\tcolor=\"no-color\";;\n+\t--dfs)\n+\t\tdo_dfs=true;;\n+\t--bfs)\n+\t\tdo_bfs=true;;\n+\t--reverse)\n+\t\treverse=\"reverse\";;\n+\t--no-reverse)\n+\t\treverse=\"no-reverse\";;\n+\t--header)\n+\t\theader=\"header\";;\n+\t--no-header)\n+\t\theader=\"no-header\";;\n+\t--body)\n+\t\tbody=\"body\";;\n+\t--no-body)\n+\t\tbody=\"no-body\";;\n+\t-*)\n+\t\techo \"Usage: tg [...] graph [--color] [--bfs|--dfs] [--reverse] [--header] [--body] [NAME]\" >&2\n+\t\texit 1;;\n+\t*)\n+\t\t[ -z \"$name\" ] ||\n+\t\t\tdie \"branch already specified ($branch)\"\n+\t\tname=\"$arg\";;\n+\tesac\n+done\n+\n+[ -z \"$name\" ] || git rev-parse --verify \"$name\" 2>/dev/null ||\n+\t\tdie \"invalid branch name: $name\"\n+\n+[ -z \"$name\" ] || name=\"-X \\\"$name\\\"\"\n+\n+[ -r \"@sharedir@\"/graph.gvpr ] || \n+\tdie \"Can't find graph.gvpr file in \\`@sharedir@'\"\n+\n+[ \"$header\" = \"header\" -o \"$body\" = \"body\" ] &&\n+\tsummary_graphviz=--graphviz=verbose\n+\n+$do_dfs && $do_bfs &&\n+\tdie \"--bfs and --dfs options are mutual exclusive\"\n+$do_bfs && stack=\"no-stack\"\n+$do_dfs && stack=\"stack\"\n+\n+type ccomps >/dev/null 2>&1 ||\n+\tdie \"need the ccomps(1) tool from the graphviz package\"\n+type gvpr >/dev/null 2>&1 ||\n+\tdie \"need the gvpr(1) tool from the graphviz package\"\n+\n+setup_pager\n+\n+$tg summary $summary_graphviz |\n+\tccomps -x $name |\n+\tgvpr -a \"$color $stack $reverse $header $body\" \\\n+\t     -f \"@sharedir@\"/graph.gvpr\ndiff --git a/tg-summary.sh b/tg-summary.sh\nindex 50ee883..ed6fda2 100644\n--- a/tg-summary.sh\n+++ b/tg-summary.sh\n@@ -5,7 +5,7 @@\n \n terse=\n graphviz=\n-\n+graphviz_verbose=\n \n ## Parse options\n \n@@ -16,6 +16,9 @@ while [ -n \"$1\" ]; do\n \t\tterse=1;;\n \t--graphviz)\n \t\tgraphviz=1;;\n+\t--graphviz=verbose)\n+\t\tgraphviz=1\n+\t\tgraphviz_verbose=1;;\n \t*)\n \t\techo \"Usage: tg [...] summary [-t | --graphviz]\" >&2\n \t\texit 1;;\n@@ -36,13 +39,13 @@ if [ -n \"$graphviz\" ]; then\n \n digraph G {\n \n-graph [\n-  rankdir = \"TB\"\n-  label=\"TopGit Layout\\n\\n\\n\"\n-  fontsize = 14\n-  labelloc=top\n-  pad = \"0.5,0.5\"\n-];\n+\tgraph [\n+\t\trankdir = \"TB\"\n+\t\tlabel=\"TopGit Layout\\n\\n\\n\"\n+\t\tfontsize = 14\n+\t\tlabelloc=top\n+\t\tpad = \"0.5,0.5\"\n+\t];\n \n EOT\n fi\n@@ -50,6 +53,11 @@ fi\n \n ## List branches\n \n+if [ -n \"$graphviz_verbose\" ]; then\n+\ttmpmsg=\"$(mktemp -t tg-summary.XXXXXX)\"\n+\ttrap 'rm -f \"$tmpmsg\"' 0\n+fi\n+\n git for-each-ref refs/top-bases |\n \twhile read rev type ref; do\n \t\tname=\"${ref#refs/top-bases/}\"\n@@ -62,12 +70,33 @@ git for-each-ref refs/top-bases |\n \t\t\tcontinue\n \t\tfi\n \t\tif [ -n \"$graphviz\" ]; then\n+\n+\t\t\tif [ -n \"$graphviz_verbose\" ]; then\n+\t\t\t\ttype=\"header\"\n+\t\t\t\tlines=0\n+\t\t\t\techo \"\\t\\\"$name\\\" [\"\n+\t\t\t\tgit cat-file blob \"$name:.topmsg\" > \"$tmpmsg\"\n+\t\t\t\twhile read line; do\n+\t\t\t\t\tl=\"$(echo \"$line\" | sed -e 's/\"/\\\\\"/g')\"\n+\t\t\t\t\t[ -z \"$line\" -a \"$type\" = \"header\" ] && {\n+\t\t\t\t\t\ttype=\"body\"\n+\t\t\t\t\t\techo \"\\t\\ttg_header_lines = \\\"$lines\\\"\"\n+\t\t\t\t\t\tlines=0\n+\t\t\t\t\t\tcontinue\n+\t\t\t\t\t}\n+\t\t\t\t\techo \"\\t\\ttg_${type}_${lines} = \\\"$l\\\"\"\n+\t\t\t\t\tlines=`expr $lines + 1`\n+\t\t\t\tdone < \"$tmpmsg\"\n+\t\t\t\techo \"\\t\\ttg_body_lines = \\\"$lines\\\"\"\n+\t\t\t\techo \"\\t]\"\n+\t\t\tfi\n+\n \t\t\tgit cat-file blob \"$name:.topdeps\" | while read dep; do\n \t\t\t\tdep_is_tgish=true\n \t\t\t\tref_exists \"refs/top-bases/$dep\"  ||\n \t\t\t\t\tdep_is_tgish=false\n \t\t\t\tif ! \"$dep_is_tgish\" || ! branch_annihilated $dep; then\n-\t\t\t\t\techo \"\\\"$name\\\" -> \\\"$dep\\\";\"\n+\t\t\t\t\techo \"\\t\\\"$name\\\" -> \\\"$dep\\\";\"\n \t\t\t\tfi\n \t\t\tdone\n \t\t\tcontinue\n-- \ntg: (9f685cd..) bw/graph (depends on: master)\n"},{"id":"114247","messageId":"20090519102742.GA28702@noris.de","threadId":"19411","inReplyTo":"1242711875-25666-1-git-send-email-bert.wesarg@googlemail.com","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Michael Radziej","fromEmail":"mir@noris.de","sentAt":"2009-05-19T10:27:43Z","receivedAt":"2009-05-19T10:27:43Z","isPatch":true,"sender":{"key":"mir@noris.de","avatar":null},"body":"Hi,\n\nI'm really looking forward for a tool like this and tried your patch\ninstantly, but I keep getting:\n\ngvpr: \"/usr/local/share/topgit/graph.gvpr\", line 936: gg_nl_ret +=\ngraph_output_commit_line();<<< \n -- cannot convert node_t to string\nError: <stdin>:16: syntax error near line 16\ncontext:  >>> \\ <<< t\"t/conflicts\" -> \"master\";\n\nI have no idea what graph.gvpr is trying. My repo is available at:\n\ngit://github.com/mradziej/topgit.git\n\nYour patch is on t/tg-graph. I tried \"tg graph\" on this repo.\n\nAny idea?\n\nCheers\n\nMichael\n\n-- \nnoris network AG - Deutschherrnstraße 15-19 - D-90429 Nürnberg -\nTel +49-911-9352-0 - Fax +49-911-9352-100\nhttp://www.noris.de - The IT-Outsourcing Company\n \nVorstand: Ingo Kraupa (Vorsitzender), Joachim Astel, Hansjochen Klenk - \nVorsitzender des Aufsichtsrats: Stefan Schnabel - AG Nürnberg HRB 17689\n"},{"id":"114249","messageId":"36ca99e90905190337y63aeb98ag59ff37688f75ba96@mail.gmail.com","threadId":"19411","inReplyTo":"20090519102742.GA28702@noris.de","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2009-05-19T10:37:25Z","receivedAt":"2009-05-19T10:37:25Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Tue, May 19, 2009 at 12:27, Michael Radziej <mir@noris.de> wrote:\n> Hi,\n>\n> I'm really looking forward for a tool like this and tried your patch\n> instantly, but I keep getting:\n>\n> gvpr: \"/usr/local/share/topgit/graph.gvpr\", line 936: gg_nl_ret +=\n> graph_output_commit_line();<<<\n>  -- cannot convert node_t to string\n> Error: <stdin>:16: syntax error near line 16\n> context:  >>> \\ <<< t\"t/conflicts\" -> \"master\";\n>\n> I have no idea what graph.gvpr is trying. My repo is available at:\n>\n> git://github.com/mradziej/topgit.git\n>\n> Your patch is on t/tg-graph. I tried \"tg graph\" on this repo.\n>\n> Any idea?\nNo, it works here. Which version of graphviz do you have?\n\nThanks,\nBert\n>\n> Cheers\n>\n> Michael\n"},{"id":"114252","messageId":"20090519110800.GB28702@noris.de","threadId":"19411","inReplyTo":"36ca99e90905190337y63aeb98ag59ff37688f75ba96@mail.gmail.com","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Michael Radziej","fromEmail":"mir@noris.de","sentAt":"2009-05-19T11:08:00Z","receivedAt":"2009-05-19T11:08:00Z","isPatch":true,"sender":{"key":"mir@noris.de","avatar":null},"body":"On Tue, May 19, Bert Wesarg wrote:\n\n> No, it works here. Which version of graphviz do you have?\n\nI tried two versions on debian:\n\n2.8-3+etch1 (debian etch)\n2.20.2-3~bpo40+1 (debian etch-backports)\n\nI also tried it on a different host running Ubuntu with\n2.16-3ubuntu2\n\nWhich one did you use?\n\nMichael\n\n-- \nnoris network AG - Deutschherrnstraße 15-19 - D-90429 Nürnberg -\nTel +49-911-9352-0 - Fax +49-911-9352-100\nhttp://www.noris.de - The IT-Outsourcing Company\n \nVorstand: Ingo Kraupa (Vorsitzender), Joachim Astel, Hansjochen Klenk - \nVorsitzender des Aufsichtsrats: Stefan Schnabel - AG Nürnberg HRB 17689\n"},{"id":"114255","messageId":"36ca99e90905190447u19d7cd19re8932f8f3aadefb2@mail.gmail.com","threadId":"19411","inReplyTo":"20090519110800.GB28702@noris.de","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2009-05-19T11:47:39Z","receivedAt":"2009-05-19T11:47:39Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Tue, May 19, 2009 at 13:08, Michael Radziej <mir@noris.de> wrote:\n> On Tue, May 19, Bert Wesarg wrote:\n>\n>> No, it works here. Which version of graphviz do you have?\n>\n> I tried two versions on debian:\n>\n> 2.8-3+etch1 (debian etch)\n> 2.20.2-3~bpo40+1 (debian etch-backports)\n>\n> I also tried it on a different host running Ubuntu with\n> 2.16-3ubuntu2\n>\n> Which one did you use?\nThe 2.20.2 one.\n\nAnd you see the error with all 3?\n\nCan you send the output from 'tg summary --graphviz' too?\n\nBert\n>\n> Michael\n"},{"id":"114256","messageId":"36ca99e90905190448k64ee18afw8abc49bd90ecb58f@mail.gmail.com","threadId":"19411","inReplyTo":"20090519110800.GB28702@noris.de","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2009-05-19T11:48:01Z","receivedAt":"2009-05-19T11:48:01Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Tue, May 19, 2009 at 13:08, Michael Radziej <mir@noris.de> wrote:\n> On Tue, May 19, Bert Wesarg wrote:\n>\n>> No, it works here. Which version of graphviz do you have?\n>\n> I tried two versions on debian:\n>\n> 2.8-3+etch1 (debian etch)\n> 2.20.2-3~bpo40+1 (debian etch-backports)\n>\n> I also tried it on a different host running Ubuntu with\n> 2.16-3ubuntu2\n>\n> Which one did you use?\nThe 2.20.2 one.\n\nAnd you see the error with all 3?\n\nCan you send the output from 'tg summary --graphviz' too?\n\nBert\n>\n> Michael\n"},{"id":"114259","messageId":"20090519131044.GD28702@noris.de","threadId":"19411","inReplyTo":"36ca99e90905190448k64ee18afw8abc49bd90ecb58f@mail.gmail.com","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Michael Radziej","fromEmail":"mir@noris.de","sentAt":"2009-05-19T13:10:44Z","receivedAt":"2009-05-19T13:10:44Z","isPatch":true,"sender":{"key":"mir@noris.de","avatar":null},"body":"On Tue, May 19, Bert Wesarg wrote:\n> And you see the error with all 3?\n\nYes\n\n> \n> Can you send the output from 'tg summary --graphviz' too?\n\n# GraphViz output; pipe to:\n#   | dot -Tpng -o <ouput>\n# or\n#   | dot -Txlib\n\ndigraph G {\n\n        graph [\n                rankdir = \"TB\"\n                label=\"TopGit Layout\\n\\n\\n\"\n                fontsize = 14\n                labelloc=top\n                pad = \"0.5,0.5\"\n        ];\n\n\\t\"t/conflicts\" -> \"master\";\n\\t\"t/python\" -> \"master\";\n\\t\"t/tg-graph\" -> \"master\";\n}\n\nMichael\n\n\n-- \nnoris network AG - Deutschherrnstraße 15-19 - D-90429 Nürnberg -\nTel +49-911-9352-0 - Fax +49-911-9352-100\nhttp://www.noris.de - The IT-Outsourcing Company\n \nVorstand: Ingo Kraupa (Vorsitzender), Joachim Astel, Hansjochen Klenk - \nVorsitzender des Aufsichtsrats: Stefan Schnabel - AG Nürnberg HRB 17689\n"},{"id":"114261","messageId":"20090519132854.GA9606@piper.oerlikon.madduck.net","threadId":"19411","inReplyTo":"1242711875-25666-1-git-send-email-bert.wesarg@googlemail.com","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"martin f krafft","fromEmail":"madduck@debian.org","sentAt":"2009-05-19T13:28:54Z","receivedAt":"2009-05-19T13:28:54Z","isPatch":true,"sender":{"key":"madduck@debian.org","avatar":null},"body":"also sprach Bert Wesarg <bert.wesarg@googlemail.com> [2009.05.19.0744 +0200]:\n> @@ -62,12 +70,33 @@ git for-each-ref refs/top-bases |\n>  \t\t\tcontinue\n>  \t\tfi\n>  \t\tif [ -n \"$graphviz\" ]; then\n> +\n> +\t\t\tif [ -n \"$graphviz_verbose\" ]; then\n> +\t\t\t\ttype=\"header\"\n> +\t\t\t\tlines=0\n> +\t\t\t\techo \"\\t\\\"$name\\\" [\"\n\nYou need to pass -e to echo for it to honour escape sequences. That\nshould solve Michael's problem. Alternatively, just use ^I directly.\n\n-- \n .''`.   martin f. krafft <madduck@d.o>      Related projects:\n: :'  :  proud Debian developer               http://debiansystem.info\n`. `'`   http://people.debian.org/~madduck    http://vcs-pkg.org\n  `-  Debian - when you have better things to do than fixing systems\n \n\"in just seven days, i can make you a man!\"\n                                      -- the rocky horror picture show\n"},{"id":"114262","messageId":"36ca99e90905190632q656a5855qe7624902ef82a3da@mail.gmail.com","threadId":"19411","inReplyTo":"20090519131044.GD28702@noris.de","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2009-05-19T13:32:00Z","receivedAt":"2009-05-19T13:32:00Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Tue, May 19, 2009 at 15:10, Michael Radziej <mir@noris.de> wrote:\n> On Tue, May 19, Bert Wesarg wrote:\n>> And you see the error with all 3?\n>\n> Yes\n>\n>>\n>> Can you send the output from 'tg summary --graphviz' too?\n>\n> # GraphViz output; pipe to:\n> #   | dot -Tpng -o <ouput>\n> # or\n> #   | dot -Txlib\n>\n> digraph G {\n>\n>        graph [\n>                rankdir = \"TB\"\n>                label=\"TopGit Layout\\n\\n\\n\"\n>                fontsize = 14\n>                labelloc=top\n>                pad = \"0.5,0.5\"\n>        ];\n>\n> \\t\"t/conflicts\" -> \"master\";\n> \\t\"t/python\" -> \"master\";\n> \\t\"t/tg-graph\" -> \"master\";\n> }\ncan you remove the '\\t' and pipe the resulting file into this command\ninside your topgit work dir:\n\ncat \"$file\" | gvpr -f share/graph.gvpr\n\nBert\n\n>\n> Michael\n"},{"id":"114263","messageId":"36ca99e90905190633l46fff979jecb61d4d0d907815@mail.gmail.com","threadId":"19411","inReplyTo":"20090519132854.GA9606@piper.oerlikon.madduck.net","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2009-05-19T13:33:16Z","receivedAt":"2009-05-19T13:33:16Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Tue, May 19, 2009 at 15:28, martin f krafft <madduck@debian.org> wrote:\n> also sprach Bert Wesarg <bert.wesarg@googlemail.com> [2009.05.19.0744 +0200]:\n>> @@ -62,12 +70,33 @@ git for-each-ref refs/top-bases |\n>>                       continue\n>>               fi\n>>               if [ -n \"$graphviz\" ]; then\n>> +\n>> +                     if [ -n \"$graphviz_verbose\" ]; then\n>> +                             type=\"header\"\n>> +                             lines=0\n>> +                             echo \"\\t\\\"$name\\\" [\"\n>\n> You need to pass -e to echo for it to honour escape sequences. That\n> should solve Michael's problem. Alternatively, just use ^I directly.\nCorrect, thanks. Looks like a feature from bash or dash to honor\nescape sequences without -e'\n\nBert\n"},{"id":"114265","messageId":"20090519133947.GE28702@noris.de","threadId":"19411","inReplyTo":"36ca99e90905190632q656a5855qe7624902ef82a3da@mail.gmail.com","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Michael Radziej","fromEmail":"mir@noris.de","sentAt":"2009-05-19T13:39:47Z","receivedAt":"2009-05-19T13:39:47Z","isPatch":true,"sender":{"key":"mir@noris.de","avatar":null},"body":"On Tue, May 19, Bert Wesarg wrote:\n\n> On Tue, May 19, 2009 at 15:10, Michael Radziej <mir@noris.de> wrote:\n> > On Tue, May 19, Bert Wesarg wrote:\n> >> And you see the error with all 3?\n> >\n> > Yes\n> >\n> >>\n> >> Can you send the output from 'tg summary --graphviz' too?\n> >\n> > # GraphViz output; pipe to:\n> > #   | dot -Tpng -o <ouput>\n> > # or\n> > #   | dot -Txlib\n> >\n> > digraph G {\n> >\n> >        graph [\n> >                rankdir = \"TB\"\n> >                label=\"TopGit Layout\\n\\n\\n\"\n> >                fontsize = 14\n> >                labelloc=top\n> >                pad = \"0.5,0.5\"\n> >        ];\n> >\n> > \\t\"t/conflicts\" -> \"master\";\n> > \\t\"t/python\" -> \"master\";\n> > \\t\"t/tg-graph\" -> \"master\";\n> > }\n> can you remove the '\\t' and pipe the resulting file into this command\n> inside your topgit work dir:\n> \n> cat \"$file\" | gvpr -f share/graph.gvpr\n\nWorks:\n\n* t/conflicts\n| * t/python\n|/  \n| * t/tg-graph\n|/  \n* master\n\n(Wow, I had no idea graphviz can produce ASCII art!)\n\nWhere's the '\\t' from? Is it a result of the \"t/\" prefix I used for the\ntopic branches?\n\nMichael\n\n-- \nnoris network AG - Deutschherrnstraße 15-19 - D-90429 Nürnberg -\nTel +49-911-9352-0 - Fax +49-911-9352-100\nhttp://www.noris.de - The IT-Outsourcing Company\n \nVorstand: Ingo Kraupa (Vorsitzender), Joachim Astel, Hansjochen Klenk - \nVorsitzender des Aufsichtsrats: Stefan Schnabel - AG Nürnberg HRB 17689\n"},{"id":"114266","messageId":"36ca99e90905190651tfed03f9ha87ca32b3aee2599@mail.gmail.com","threadId":"19411","inReplyTo":"20090519133947.GE28702@noris.de","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2009-05-19T13:51:34Z","receivedAt":"2009-05-19T13:51:34Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Tue, May 19, 2009 at 15:39, Michael Radziej <mir@noris.de> wrote:\n> On Tue, May 19, Bert Wesarg wrote:\n>\n>> On Tue, May 19, 2009 at 15:10, Michael Radziej <mir@noris.de> wrote:\n>> > On Tue, May 19, Bert Wesarg wrote:\n>> >> And you see the error with all 3?\n>> >\n>> > Yes\n>> >\n>> >>\n>> >> Can you send the output from 'tg summary --graphviz' too?\n>> >\n>> > # GraphViz output; pipe to:\n>> > #   | dot -Tpng -o <ouput>\n>> > # or\n>> > #   | dot -Txlib\n>> >\n>> > digraph G {\n>> >\n>> >        graph [\n>> >                rankdir = \"TB\"\n>> >                label=\"TopGit Layout\\n\\n\\n\"\n>> >                fontsize = 14\n>> >                labelloc=top\n>> >                pad = \"0.5,0.5\"\n>> >        ];\n>> >\n>> > \\t\"t/conflicts\" -> \"master\";\n>> > \\t\"t/python\" -> \"master\";\n>> > \\t\"t/tg-graph\" -> \"master\";\n>> > }\n>> can you remove the '\\t' and pipe the resulting file into this command\n>> inside your topgit work dir:\n>>\n>> cat \"$file\" | gvpr -f share/graph.gvpr\n>\n> Works:\n>\n> * t/conflicts\n> | * t/python\n> |/\n> | * t/tg-graph\n> |/\n> * master\n>\n> (Wow, I had no idea graphviz can produce ASCII art!)\n>\n> Where's the '\\t' from? Is it a result of the \"t/\" prefix I used for the\n> topic branches?\nNo its just a formating tab to make the output of summary --graphviz nicer ;-)\n\nAs martin suggests, you can remove any \"\\t\" from the 'echo' lines in\ntg-summary or add a '-e' option to it.\n\nI will probably post tomorrow an updated patch.\n\nBert\n>\n> Michael\n"},{"id":"114268","messageId":"20090519140920.GF28702@noris.de","threadId":"19411","inReplyTo":"36ca99e90905190651tfed03f9ha87ca32b3aee2599@mail.gmail.com","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Michael Radziej","fromEmail":"mir@noris.de","sentAt":"2009-05-19T14:09:20Z","receivedAt":"2009-05-19T14:09:20Z","isPatch":true,"sender":{"key":"mir@noris.de","avatar":null},"body":"On Tue, May 19, Bert Wesarg wrote:\n\n> No its just a formating tab to make the output of summary --graphviz nicer ;-)\n> \n> As martin suggests, you can remove any \"\\t\" from the 'echo' lines in\n> tg-summary or add a '-e' option to it.\n> \n> I will probably post tomorrow an updated patch.\n\nCool. If I had any knowledge of graphviz I'd really like to help out :-(\n\nI see another issue. On a quite complicated repository, I did the \"tg\nsummary --graphviz ... edit ...  graphviz ...\" dance, and there I get the\nfollowing output:\n\nmir@mir:otrs-git$ cat /tmp/graph | gvpr -f ~/src/topgit/share/graph.gvpr\ngvpr: \"/home/mir/src/topgit/share/graph.gvpr\", line 936: gg_nl_ret +=\ngraph_output_commit_line();<<<\n -- cannot convert node_t to string\n\nI have attached /tmp/graph (the output of tg summary --graphviz), I can't\nfind anything that would stick out. I have to prepare this particular\nproject a bit before I could push it out to git hub (removing configuration\npart with not-so-public content), do you need it at all?\n\nI'm going to be away from mail for a few hours.\n\n\nThanks for your efforts!\n\nMichael\n\n\n-- \nnoris network AG - Deutschherrnstraße 15-19 - D-90429 Nürnberg -\nTel +49-911-9352-0 - Fax +49-911-9352-100\nhttp://www.noris.de - The IT-Outsourcing Company\n \nVorstand: Ingo Kraupa (Vorsitzender), Joachim Astel, Hansjochen Klenk - \nVorsitzender des Aufsichtsrats: Stefan Schnabel - AG Nürnberg HRB 17689\n\n\n# GraphViz output; pipe to:\n#   | dot -Tpng -o <ouput>\n# or\n#   | dot -Txlib\n\ndigraph G {\n\n\tgraph [\n\t\trankdir = \"TB\"\n\t\tlabel=\"TopGit Layout\\n\\n\\n\"\n\t\tfontsize = 14\n\t\tlabelloc=top\n\t\tpad = \"0.5,0.5\"\n\t];\n\n\"t/AgentLinkObject-permissions\" -> \"patchbase\";\n\"t/AgentTicketForward-quoting\" -> \"patchbase\";\n\"t/FilterTextTicket\" -> \"patchbase\";\n\"t/TicketCreate-with-TimeStamp\" -> \"patchbase\";\n\"t/acl-notowner\" -> \"patchbase\";\n\"t/add-customer-id-checking\" -> \"patchbase\";\n\"t/agent-email-in-forward-sender\" -> \"patchbase\";\n\"t/agent-name-in-sender+noris-followups\" -> \"t/agent-name-in-sender-v2\";\n\"t/agent-name-in-sender+noris-followups\" -> \"t/noris-followups\";\n\"t/agent-name-in-sender-v2\" -> \"patchbase\";\n\"t/agent-notification-from\" -> \"t/mail-outgoing-envelope-sender\";\n\"t/auth-http-with-ldap\" -> \"patchbase\";\n\"t/auth-ldap-with-UserSyncLDAPRoles\" -> \"patchbase\";\n\"t/auto-responses-to-envelope-sender\" -> \"patchbase\";\n\"t/backport-references-headers\" -> \"patchbase\";\n\"t/backport-references-headers-conflicts\" -> \"t/backport-references-headers\";\n\"t/backport-references-headers-conflicts\" -> \"t/TicketCreate-with-TimeStamp\";\n\"t/backport-references-headers-conflicts\" -> \"t/escalate-on-freetime\";\n\"t/backport-references-headers-conflicts\" -> \"t/agent-notification-from\";\n\"t/backport-references-headers-conflicts\" -> \"t/agent-email-in-forward-sender\";\n\"t/backport-references-headers-conflicts\" -> \"t/noris-ticket-notowner\";\n\"t/backport-references-headers-conflicts\" -> \"t/noris-self-ticket+noris-followups\";\n\"t/backport-references-headers-conflicts\" -> \"t/internal-reply-without-envelope-sender-address\";\n\"t/backport-references-headers-conflicts\" -> \"t/agent-name-in-sender+noris-followups\";\n\"t/backport-references-headers-conflicts\" -> \"t/references-headers-for-internal-reply\";\n\"t/backport-references-headers-conflicts\" -> \"t/bare-localparts+http-customer-backend\";\n\"t/bare-localparts\" -> \"patchbase\";\n\"t/bare-localparts+http-customer-backend\" -> \"t/bare-localparts\";\n\"t/bare-localparts+http-customer-backend\" -> \"t/http-customer-backend\";\n\"t/canned-response-format\" -> \"patchbase\";\n\"t/clean-subject\" -> \"patchbase\";\n\"t/clean-subject+ticket-after-subject\" -> \"t/clean-subject\";\n\"t/clean-subject+ticket-after-subject\" -> \"t/ticket-after-subject\";\n\"t/codefix-AgentTicketQueue\" -> \"patchbase\";\n\"t/compose-if-without-to\" -> \"patchbase\";\n\"t/compose-without-envelope-sender-address\" -> \"t/mail-outgoing-envelope-sender\";\n\"t/configurable-max-length-in-templates\" -> \"patchbase\";\n\"t/count-available-tickets\" -> \"patchbase\";\n\"t/create-ticket-with-tn\" -> \"t/internal-reply\";\n\"t/db-ticket-number\" -> \"patchbase\";\n\"t/debian-apache\" -> \"patchbase\";\n\"t/debian-backup\" -> \"patchbase\";\n\"t/debian-cron\" -> \"patchbase\";\n\"t/debian-misc-upstream\" -> \"patchbase\";\n\"t/debian-noris\" -> \"t/debian-org\";\n\"t/debian-opt\" -> \"patchbase\";\n\"t/debian-org\" -> \"patchbase\";\n\"t/debian-postmaster\" -> \"patchbase\";\n\"t/default-ticket-type\" -> \"patchbase\";\n\"t/escalate-on-freetime\" -> \"patchbase\";\n\"t/escalation-search\" -> \"patchbase\";\n\"t/event-stateupdate-with-previous-state\" -> \"patchbase\";\n\"t/favicon\" -> \"patchbase\";\n\"t/favicon+noris-agent-login\" -> \"t/favicon\";\n\"t/favicon+noris-agent-login\" -> \"t/noris-agent-login\";\n\"t/favicon+noris-customer-interface\" -> \"t/favicon\";\n\"t/favicon+noris-customer-interface\" -> \"t/noris-customer-interface\";\n\"t/favicon+noris-customer-interface+noris-agent-login\" -> \"t/favicon\";\n\"t/favicon+noris-customer-interface+noris-agent-login\" -> \"t/noris-customer-interface\";\n\"t/favicon+noris-customer-interface+noris-agent-login\" -> \"t/noris-agent-login\";\n\"t/fix-AgentTicketZoom-HighlightColor\" -> \"patchbase\";\n\"t/fix-CustomerAge-spaces\" -> \"patchbase\";\n\"t/fix-Layout-CheckMimeType\" -> \"patchbase\";\n\"t/fix-download-contenttype\" -> \"patchbase\";\n\"t/fix-encode-convert\" -> \"patchbase\";\n\"t/fix-new-messages\" -> \"patchbase\";\n\"t/fix-nostates-sql\" -> \"patchbase\";\n\"t/fix-search-index\" -> \"patchbase\";\n\"t/fix-ticket-search\" -> \"patchbase\";\n\"t/fix-ticketfreetime-with-attachments\" -> \"t/internal-reply\";\n\"t/forward-as-rfc822\" -> \"patchbase\";\n\"t/forward-template\" -> \"patchbase\";\n\"t/freetext-fields-size\" -> \"patchbase\";\n\"t/http-customer-backend\" -> \"patchbase\";\n\"t/http-customer-backend+t/agent-name-in-sender-v2\" -> \"t/http-customer-backend\";\n\"t/http-customer-backend+t/agent-name-in-sender-v2\" -> \"t/agent-name-in-sender-v2\";\n\"t/http-customer-backend-with-ticket-map\" -> \"t/http-customer-backend\";\n\"t/internal-reply\" -> \"t/noris-followups\";\n\"t/internal-reply-without-envelope-sender-address\" -> \"t/internal-reply\";\n\"t/keep-pending-time\" -> \"patchbase\";\n\"t/keep-pending-time\" -> \"t/internal-reply\";\n\"t/links-to-closed-tickets\" -> \"patchbase\";\n\"t/longer-freetext-fields\" -> \"patchbase\";\n\"t/longer-queue-names\" -> \"t/templates-with-config-maxlength\";\n\"t/mail-outgoing-envelope-sender\" -> \"patchbase\";\n\"t/max-title-length\" -> \"patchbase\";\n\"t/more-article-actions\" -> \"patchbase\";\n\"t/more-params-in-AgentTicketEmail\" -> \"patchbase\";\n\"t/more-params-in-AgentTicketPhone\" -> \"t/internal-reply\";\n\"t/move-as-nonowner\" -> \"patchbase\";\n\"t/no-accounted-time\" -> \"patchbase\";\n\"t/no-autoreply-to-agent\" -> \"patchbase\";\n\"t/no-autoreply-to-agent+http-customer-backend\" -> \"t/no-autoreply-to-agent\";\n\"t/no-autoreply-to-agent+http-customer-backend\" -> \"t/http-customer-backend\";\n\"t/no-cc-with-auto-response\" -> \"patchbase\";\n\"t/no-redirect-on-close\" -> \"patchbase\";\n\"t/no-redirect-on-close\" -> \"t/internal-reply\";\n\"t/no-redirect-on-close\" -> \"t/parking-states\";\n\"t/no-redirect-on-close\" -> \"t/backport-references-headers\";\n\"t/no-search-on-link\" -> \"patchbase\";\n\"t/noris-FilterTextWiki\" -> \"patchbase\";\n\"t/noris-add-work-time\" -> \"patchbase\";\n\"t/noris-agent-login\" -> \"patchbase\";\n\"t/noris-attributes\" -> \"patchbase\";\n\"t/noris-config\" -> \"patchbase\";\n\"t/noris-confitems\" -> \"patchbase\";\n\"t/noris-customer-interface\" -> \"patchbase\";\n\"t/noris-docushare\" -> \"patchbase\";\n\"t/noris-followups\" -> \"t/more-article-actions\";\n\"t/noris-followups\" -> \"t/mail-outgoing-envelope-sender\";\n\"t/noris-followups\" -> \"t/more-params-in-AgentTicketEmail\";\n\"t/noris-meine-tickets\" -> \"t/parking-states\";\n\"t/noris-rt-followups\" -> \"t/noris-followups\";\n\"t/noris-self-ticket\" -> \"patchbase\";\n\"t/noris-self-ticket+noris-followups\" -> \"t/noris-self-ticket\";\n\"t/noris-self-ticket+noris-followups\" -> \"t/noris-rt-followups\";\n\"t/noris-take\" -> \"patchbase\";\n\"t/noris-ticket-notowner\" -> \"t/internal-reply\";\n\"t/noris-translation\" -> \"patchbase\";\n\"t/noris-treeview\" -> \"patchbase\";\n\"t/parking-states\" -> \"patchbase\";\n\"t/queue-by-envelope-recipient\" -> \"patchbase\";\n\"t/rcs-keywords\" -> \"patchbase\";\n\"t/recipients-for-replies\" -> \"t/internal-reply\";\n\"t/references-headers-for-internal-reply\" -> \"t/internal-reply\";\n\"t/show-last-article\" -> \"patchbase\";\n\"t/suppress-no-caching-headers\" -> \"t/internal-reply\";\n\"t/suppress-no-caching-headers\" -> \"t/parking-states\";\n\"t/suppress-no-caching-headers\" -> \"t/noris-rt-followups\";\n\"t/templates-with-config-maxlength\" -> \"patchbase\";\n\"t/testing\" -> \"patchbase\";\n\"t/testing\" -> \"t/forward-template\";\n\"t/testing\" -> \"t/AgentLinkObject-permissions\";\n\"t/testing\" -> \"t/acl-notowner\";\n\"t/testing\" -> \"t/debian-apache\";\n\"t/testing\" -> \"t/debian-backup\";\n\"t/testing\" -> \"t/debian-cron\";\n\"t/testing\" -> \"t/debian-misc-upstream\";\n\"t/testing\" -> \"t/debian-noris\";\n\"t/testing\" -> \"t/debian-opt\";\n\"t/testing\" -> \"t/debian-postmaster\";\n\"t/testing\" -> \"t/default-ticket-type\";\n\"t/testing\" -> \"t/escalation-search\";\n\"t/testing\" -> \"t/event-stateupdate-with-previous-state\";\n\"t/testing\" -> \"t/fix-download-contenttype\";\n\"t/testing\" -> \"t/fix-encode-convert\";\n\"t/testing\" -> \"t/fix-nostates-sql\";\n\"t/testing\" -> \"t/fix-ticket-search\";\n\"t/testing\" -> \"t/forward-as-rfc822\";\n\"t/testing\" -> \"t/http-customer-backend+t/agent-name-in-sender-v2\";\n\"t/testing\" -> \"t/links-to-closed-tickets\";\n\"t/testing\" -> \"t/move-as-nonowner\";\n\"t/testing\" -> \"t/no-search-on-link\";\n\"t/testing\" -> \"t/noris-config\";\n\"t/testing\" -> \"t/noris-confitems\";\n\"t/testing\" -> \"t/noris-docushare\";\n\"t/testing\" -> \"t/noris-meine-tickets\";\n\"t/testing\" -> \"t/noris-treeview\";\n\"t/testing\" -> \"t/show-last-article\";\n\"t/testing\" -> \"t/ticketmailbox-by-queue\";\n\"t/testing\" -> \"t/workaround-mime-words\";\n\"t/testing\" -> \"t/noris-take\";\n\"t/testing\" -> \"t/noris-translation\";\n\"t/testing\" -> \"t/noris-attributes\";\n\"t/testing\" -> \"t/auto-responses-to-envelope-sender\";\n\"t/testing\" -> \"t/ticket-in-localpart\";\n\"t/testing\" -> \"t/no-cc-with-auto-response\";\n\"t/testing\" -> \"t/auth-http-with-ldap\";\n\"t/testing\" -> \"t/backport-references-headers-conflicts\";\n\"t/testing\" -> \"t/compose-without-envelope-sender-address\";\n\"t/testing\" -> \"t/noris-add-work-time\";\n\"t/testing\" -> \"t/no-redirect-on-close\";\n\"t/testing\" -> \"t/rcs-keywords\";\n\"t/testing\" -> \"t/ticket-subject-hook+db-ticket-number\";\n\"t/testing\" -> \"t/fix-search-index\";\n\"t/testing\" -> \"t/queue-by-envelope-recipient\";\n\"t/testing\" -> \"t/compose-if-without-to\";\n\"t/testing\" -> \"t/no-accounted-time\";\n\"t/testing\" -> \"t/longer-freetext-fields\";\n\"t/testing\" -> \"t/add-customer-id-checking\";\n\"t/testing\" -> \"t/auth-ldap-with-UserSyncLDAPRoles\";\n\"t/testing\" -> \"t/canned-response-format\";\n\"t/testing\" -> \"t/clean-subject+ticket-after-subject\";\n\"t/testing\" -> \"t/more-params-in-AgentTicketPhone\";\n\"t/testing\" -> \"t/fix-new-messages\";\n\"t/testing\" -> \"t/keep-pending-time\";\n\"t/testing\" -> \"t/create-ticket-with-tn\";\n\"t/testing\" -> \"t/FilterTextTicket\";\n\"t/testing\" -> \"t/noris-FilterTextWiki\";\n\"t/testing\" -> \"t/suppress-no-caching-headers\";\n\"t/testing\" -> \"t/bare-localparts\";\n\"t/testing\" -> \"t/bare-localparts+http-customer-backend\";\n\"t/testing\" -> \"t/count-available-tickets\";\n\"t/testing\" -> \"t/recipients-for-replies\";\n\"t/testing\" -> \"t/no-autoreply-to-agent\";\n\"t/testing\" -> \"t/no-autoreply-to-agent+http-customer-backend\";\n\"t/testing\" -> \"t/http-customer-backend-with-ticket-map\";\n\"t/testing\" -> \"t/fix-CustomerAge-spaces\";\n\"t/testing\" -> \"t/freetext-fields-size\";\n\"t/testing\" -> \"t/fix-ticketfreetime-with-attachments\";\n\"t/testing\" -> \"t/configurable-max-length-in-templates\";\n\"t/testing\" -> \"t/max-title-length\";\n\"t/testing\" -> \"t/favicon+noris-customer-interface+noris-agent-login\";\n\"t/testing\" -> \"t/AgentTicketForward-quoting\";\n\"t/testing\" -> \"t/fix-AgentTicketZoom-HighlightColor\";\n\"t/testing\" -> \"t/ticketfreetext-per-queue\";\n\"t/testing\" -> \"t/fix-Layout-CheckMimeType\";\n\"t/testing\" -> \"t/longer-queue-names\";\n\"t/ticket-after-subject\" -> \"patchbase\";\n\"t/ticket-hook-subject\" -> \"patchbase\";\n\"t/ticket-in-localpart\" -> \"patchbase\";\n\"t/ticket-subject-hook+db-ticket-number\" -> \"t/ticket-hook-subject\";\n\"t/ticket-subject-hook+db-ticket-number\" -> \"t/db-ticket-number\";\n\"t/ticket-subject-hook+db-ticket-number\" -> \"t/clean-subject\";\n\"t/ticketfreetext-per-queue\" -> \"t/backport-references-headers-conflicts\";\n\"t/ticketfreetext-per-queue\" -> \"t/parking-states\";\n\"t/ticketmailbox-by-queue\" -> \"t/parking-states\";\n\"t/ticketmailbox-by-queue\" -> \"t/codefix-AgentTicketQueue\";\n\"t/ticketmailbox-by-queue\" -> \"t/ticketmailbox-sort-array\";\n\"t/ticketmailbox-sort-array\" -> \"patchbase\";\n\"t/workaround-mime-words\" -> \"patchbase\";\n}\n"},{"id":"114269","messageId":"36ca99e90905190714l15a13483p2a76ab7f630bd8bb@mail.gmail.com","threadId":"19411","inReplyTo":"20090519140920.GF28702@noris.de","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2009-05-19T14:14:25Z","receivedAt":"2009-05-19T14:14:25Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Tue, May 19, 2009 at 16:09, Michael Radziej <mir@noris.de> wrote:\n> On Tue, May 19, Bert Wesarg wrote:\n>\n>> No its just a formating tab to make the output of summary --graphviz nicer ;-)\n>>\n>> As martin suggests, you can remove any \"\\t\" from the 'echo' lines in\n>> tg-summary or add a '-e' option to it.\n>>\n>> I will probably post tomorrow an updated patch.\n>\n> Cool. If I had any knowledge of graphviz I'd really like to help out :-(\n>\n> I see another issue. On a quite complicated repository, I did the \"tg\n> summary --graphviz ... edit ...  graphviz ...\" dance, and there I get the\n> following output:\n>\n> mir@mir:otrs-git$ cat /tmp/graph | gvpr -f ~/src/topgit/share/graph.gvpr\n> gvpr: \"/home/mir/src/topgit/share/graph.gvpr\", line 936: gg_nl_ret +=\n> graph_output_commit_line();<<<\n>  -- cannot convert node_t to string\nOk, still two unrelated problems.\n\n>\n> I have attached /tmp/graph (the output of tg summary --graphviz), I can't\n> find anything that would stick out. I have to prepare this particular\n> project a bit before I could push it out to git hub (removing configuration\n> part with not-so-public content), do you need it at all?\nI think the graphviz output should suffice. thanks.\n\n>\n> I'm going to be away from mail for a few hours.\n>\n>\n> Thanks for your efforts!\n>\n> Michael\n"},{"id":"114271","messageId":"36ca99e90905190732g53599cb8y142a137bc79ad655@mail.gmail.com","threadId":"19411","inReplyTo":"36ca99e90905190714l15a13483p2a76ab7f630bd8bb@mail.gmail.com","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2009-05-19T14:32:21Z","receivedAt":"2009-05-19T14:32:21Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Tue, May 19, 2009 at 16:14, Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n> On Tue, May 19, 2009 at 16:09, Michael Radziej <mir@noris.de> wrote:\n>> I see another issue. On a quite complicated repository, I did the \"tg\n>> summary --graphviz ... edit ...  graphviz ...\" dance, and there I get the\n>> following output:\n>>\n>> mir@mir:otrs-git$ cat /tmp/graph | gvpr -f ~/src/topgit/share/graph.gvpr\n>> gvpr: \"/home/mir/src/topgit/share/graph.gvpr\", line 936: gg_nl_ret +=\n>> graph_output_commit_line();<<<\n>>  -- cannot convert node_t to string\n> Ok, still two unrelated problems.\nAnd still I can't reproduce it here :(\n\nBert\n"},{"id":"114284","messageId":"36ca99e90905190945m1ecea6bka99946783ea1d22a@mail.gmail.com","threadId":"19411","inReplyTo":"36ca99e90905190732g53599cb8y142a137bc79ad655@mail.gmail.com","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2009-05-19T16:45:10Z","receivedAt":"2009-05-19T16:45:10Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Tue, May 19, 2009 at 16:32, Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n> On Tue, May 19, 2009 at 16:14, Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n>> On Tue, May 19, 2009 at 16:09, Michael Radziej <mir@noris.de> wrote:\n>>> I see another issue. On a quite complicated repository, I did the \"tg\n>>> summary --graphviz ... edit ...  graphviz ...\" dance, and there I get the\n>>> following output:\n>>>\n>>> mir@mir:otrs-git$ cat /tmp/graph | gvpr -f ~/src/topgit/share/graph.gvpr\n>>> gvpr: \"/home/mir/src/topgit/share/graph.gvpr\", line 936: gg_nl_ret +=\n>>> graph_output_commit_line();<<<\n>>>  -- cannot convert node_t to string\n>> Ok, still two unrelated problems.\n> And still I can't reproduce it here :(\nI found a missing rename which causes random segfaults.\n\nHere the diff:\n\n--- a/share/graph.gvpr\n+++ b/share/graph.gvpr\n@@ -523,7 +523,7 @@ BEGIN {\n                  * print the branch lines as \"|\".\n                  */\n                 if (gg_prev_state == GRAPH_POST_MERGE &&\n-                    gg_prev_commit_index < gg_opcl_i)\n+                    gg_prev_n_index < gg_opcl_i)\n                     gg_opcl_sb += gg_write_column(gg_columns[gg_opcl_i], \"\\\\\");\n                 else\n                     gg_opcl_sb += gg_write_column(gg_columns[gg_opcl_i], \"|\");\n\npatch tomorrow\n\nBert\n"},{"id":"114294","messageId":"20090519184402.GA27352@pengutronix.de","threadId":"19411","inReplyTo":"36ca99e90905190633l46fff979jecb61d4d0d907815@mail.gmail.com","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Uwe Kleine-König","fromEmail":"u.kleine-koenig@pengutronix.de","sentAt":"2009-05-19T18:44:02Z","receivedAt":"2009-05-19T18:44:02Z","isPatch":true,"sender":{"key":"u.kleine-koenig@pengutronix.de","avatar":"https://gravatar.com/avatar/354b5e3ceb2806a2f1e1e382ac29ddbdad18288654da62b61eb13583a857eee7?d=mp&s=160"},"body":"Hi Bert,\n\nOn Tue, May 19, 2009 at 03:33:16PM +0200, Bert Wesarg wrote:\n> On Tue, May 19, 2009 at 15:28, martin f krafft <madduck@debian.org> wrote:\n> > also sprach Bert Wesarg <bert.wesarg@googlemail.com> [2009.05.19.0744 +0200]:\n> >> @@ -62,12 +70,33 @@ git for-each-ref refs/top-bases |\n> >>                       continue\n> >>               fi\n> >>               if [ -n \"$graphviz\" ]; then\n> >> +\n> >> +                     if [ -n \"$graphviz_verbose\" ]; then\n> >> +                             type=\"header\"\n> >> +                             lines=0\n> >> +                             echo \"\\t\\\"$name\\\" [\"\n> >\n> > You need to pass -e to echo for it to honour escape sequences. That\n> > should solve Michael's problem. Alternatively, just use ^I directly.\n> Correct, thanks. Looks like a feature from bash or dash to honor\n> escape sequences without -e'\nquoting\nhttp://www.gnu.org/software/hello/manual/autoconf/Limitations-of-Builtins.html#Limitations-of-Builtins:\n\n\tIt is not possible to use `echo' portably unless both options\n\tand escape sequences are omitted.\n\n... use printf instead.\n\nBest regards\nUwe\n\n-- \nPengutronix e.K.                              | Uwe Kleine-König            |\nIndustrial Linux Solutions                    | http://www.pengutronix.de/  |\n"},{"id":"114320","messageId":"36ca99e90905192307m67c11b83y7af44ccdb48f43e0@mail.gmail.com","threadId":"19411","inReplyTo":"20090519184402.GA27352@pengutronix.de","subject":"Re: [TopGit PATCH] tg-graph: print dependency graph like git log --graph","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2009-05-20T06:07:05Z","receivedAt":"2009-05-20T06:07:05Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"2009/5/19 Uwe Kleine-König <u.kleine-koenig@pengutronix.de>:\n> Hi Bert,\n>\n> On Tue, May 19, 2009 at 03:33:16PM +0200, Bert Wesarg wrote:\n>> On Tue, May 19, 2009 at 15:28, martin f krafft <madduck@debian.org> wrote:\n>> > also sprach Bert Wesarg <bert.wesarg@googlemail.com> [2009.05.19.0744 +0200]:\n>> >> @@ -62,12 +70,33 @@ git for-each-ref refs/top-bases |\n>> >>                       continue\n>> >>               fi\n>> >>               if [ -n \"$graphviz\" ]; then\n>> >> +\n>> >> +                     if [ -n \"$graphviz_verbose\" ]; then\n>> >> +                             type=\"header\"\n>> >> +                             lines=0\n>> >> +                             echo \"\\t\\\"$name\\\" [\"\n>> >\n>> > You need to pass -e to echo for it to honour escape sequences. That\n>> > should solve Michael's problem. Alternatively, just use ^I directly.\n>> Correct, thanks. Looks like a feature from bash or dash to honor\n>> escape sequences without -e'\n> quoting\n> http://www.gnu.org/software/hello/manual/autoconf/Limitations-of-Builtins.html#Limitations-of-Builtins:\n>\n>        It is not possible to use `echo' portably unless both options\n>        and escape sequences are omitted.\n>\n> ... use printf instead.\nThank you for this valuable link, Will switch to printf.\n\nBert\n>\n> Best regards\n> Uwe\n"}]}