diff --git a/Documentation/config/color.txt b/Documentation/config/color.txt index 2f2275ac69..cf8b03add4 100644 --- a/Documentation/config/color.txt +++ b/Documentation/config/color.txt @@ -74,10 +74,34 @@ color.diff.:: `oldBold`, and `newBold` (see linkgit:git-range-diff[1] for details). color.decorate.:: - Use customized color for 'git log --decorate' output. `` is one - of `branch`, `remoteBranch`, `tag`, `stash` or `HEAD` for local - branches, remote-tracking branches, tags, stash and HEAD, respectively - and `grafted` for grafted commits. + Use customized color for the output of 'git log --decorate' as well as + the `%d`, `%D` and `%(decorate)` placeholders in custom log formats, + whereby `` specifies which decoration elements the color applies + to: ++ +-- +`HEAD`;; + the current HEAD +`branch`;; + local branches +`remoteBranch`;; + remote-tracking branches +`tag`;; + lightweight and annotated tags +`stash`;; + the stash ref +`ref`;; + any other refs (not shown by default) +`pseudoref`;; + pseudorefs such as ORIG_HEAD or MERGE_HEAD (not shown by default) +`grafted`;; + grafted and replaced commits +`symbol`;; + punctuation symbols surrounding the other elements +-- ++ +(Variable `log.initialDecorationSet` or linkgit:git-log[1] option +`--clear-decorations` can be used to show all refs and pseudorefs.) color.grep:: When set to `always`, always highlight matches. When `false` (or diff --git a/commit.h b/commit.h index 1cc872f225..ad12200d40 100644 --- a/commit.h +++ b/commit.h @@ -54,8 +54,11 @@ enum decoration_type { DECORATION_REF_REMOTE, DECORATION_REF_TAG, DECORATION_REF_STASH, + DECORATION_REF, DECORATION_REF_HEAD, + DECORATION_REF_PSEUDO, DECORATION_GRAFTED, + DECORATION_SYMBOL, }; void add_name_decoration(enum decoration_type type, const char *name, struct object *obj); diff --git a/log-tree.c b/log-tree.c index 504da6b519..65ebb74d40 100644 --- a/log-tree.c +++ b/log-tree.c @@ -34,13 +34,16 @@ static int decoration_loaded; static int decoration_flags; static char decoration_colors[][COLOR_MAXLEN] = { - GIT_COLOR_RESET, - GIT_COLOR_BOLD_GREEN, /* REF_LOCAL */ - GIT_COLOR_BOLD_RED, /* REF_REMOTE */ - GIT_COLOR_BOLD_YELLOW, /* REF_TAG */ - GIT_COLOR_BOLD_MAGENTA, /* REF_STASH */ - GIT_COLOR_BOLD_CYAN, /* REF_HEAD */ - GIT_COLOR_BOLD_BLUE, /* GRAFTED */ + [DECORATION_NONE] = GIT_COLOR_RESET, + [DECORATION_REF_LOCAL] = GIT_COLOR_BOLD_GREEN, + [DECORATION_REF_REMOTE] = GIT_COLOR_BOLD_RED, + [DECORATION_REF_TAG] = GIT_COLOR_BOLD_YELLOW, + [DECORATION_REF_STASH] = GIT_COLOR_BOLD_MAGENTA, + [DECORATION_REF] = GIT_COLOR_BOLD_MAGENTA, + [DECORATION_REF_HEAD] = GIT_COLOR_BOLD_CYAN, + [DECORATION_REF_PSEUDO] = GIT_COLOR_BOLD_CYAN, + [DECORATION_GRAFTED] = GIT_COLOR_BOLD_BLUE, + [DECORATION_SYMBOL] = GIT_COLOR_NIL, }; static const char *color_decorate_slots[] = { @@ -48,8 +51,11 @@ static const char *color_decorate_slots[] = { [DECORATION_REF_REMOTE] = "remoteBranch", [DECORATION_REF_TAG] = "tag", [DECORATION_REF_STASH] = "stash", + [DECORATION_REF] = "ref", [DECORATION_REF_HEAD] = "HEAD", + [DECORATION_REF_PSEUDO] = "pseudoref", [DECORATION_GRAFTED] = "grafted", + [DECORATION_SYMBOL] = "symbol", }; static const char *decorate_get_color(int decorate_use_color, enum decoration_type ix) @@ -149,7 +155,7 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid, int i; struct object *obj; enum object_type objtype; - enum decoration_type deco_type = DECORATION_NONE; + enum decoration_type deco_type = DECORATION_REF; struct decoration_filter *filter = (struct decoration_filter *)cb_data; const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref; @@ -204,6 +210,27 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid, return 0; } +static int add_pseudoref_decoration(const char *refname, + const struct object_id *oid, + int flags UNUSED, + void *cb_data) +{ + struct object *obj; + enum object_type objtype; + struct decoration_filter *filter = (struct decoration_filter *)cb_data; + + if (filter && !ref_filter_match(refname, filter)) + return 0; + + objtype = oid_object_info(the_repository, oid, NULL); + if (objtype < 0) + return 0; + + obj = lookup_object_by_type(the_repository, oid, objtype); + add_name_decoration(DECORATION_REF_PSEUDO, refname, obj); + return 0; +} + static int add_graft_decoration(const struct commit_graft *graft, void *cb_data UNUSED) { @@ -232,6 +259,7 @@ void load_ref_decorations(struct decoration_filter *filter, int flags) decoration_loaded = 1; decoration_flags = flags; for_each_ref(add_ref_decoration, filter); + for_each_pseudoref(add_pseudoref_decoration, filter); head_ref(add_ref_decoration, filter); for_each_commit_graft(add_graft_decoration, filter); } @@ -312,7 +340,7 @@ void format_decorations(struct strbuf *sb, { const struct name_decoration *decoration; const struct name_decoration *current_and_HEAD; - const char *color_commit, *color_reset; + const char *color_symbol, *color_reset; const char *prefix = " ("; const char *suffix = ")"; @@ -337,7 +365,10 @@ void format_decorations(struct strbuf *sb, tag = opts->tag; } - color_commit = diff_get_color(use_color, DIFF_COMMIT); + color_symbol = decorate_get_color(use_color, DECORATION_SYMBOL); + if (color_is_nil(color_symbol)) + color_symbol = diff_get_color(use_color, DIFF_COMMIT); + color_reset = decorate_get_color(use_color, DECORATION_NONE); current_and_HEAD = current_pointed_by_HEAD(decoration); @@ -352,7 +383,7 @@ void format_decorations(struct strbuf *sb, decorate_get_color(use_color, decoration->type); if (*prefix) { - strbuf_addstr(sb, color_commit); + strbuf_addstr(sb, color_symbol); strbuf_addstr(sb, prefix); strbuf_addstr(sb, color_reset); } @@ -369,7 +400,7 @@ void format_decorations(struct strbuf *sb, if (current_and_HEAD && decoration->type == DECORATION_REF_HEAD) { - strbuf_addstr(sb, color_commit); + strbuf_addstr(sb, color_symbol); strbuf_addstr(sb, pointer); strbuf_addstr(sb, color_reset); strbuf_addstr(sb, decorate_get_color(use_color, current_and_HEAD->type)); @@ -382,7 +413,7 @@ void format_decorations(struct strbuf *sb, decoration = decoration->next; } if (*suffix) { - strbuf_addstr(sb, color_commit); + strbuf_addstr(sb, color_symbol); strbuf_addstr(sb, suffix); strbuf_addstr(sb, color_reset); } diff --git a/refs.c b/refs.c index 16bfa21df7..eab8b5dbda 100644 --- a/refs.c +++ b/refs.c @@ -65,6 +65,21 @@ static unsigned char refname_disposition[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4 }; +/* + * List of documented pseudorefs. This needs to be kept in sync with the list + * in Documentation/revisions.txt. + */ +static const char *const pseudorefs[] = { + "FETCH_HEAD", + "ORIG_HEAD", + "MERGE_HEAD", + "REBASE_HEAD", + "CHERRY_PICK_HEAD", + "REVERT_HEAD", + "BISECT_HEAD", + "AUTO_MERGE", +}; + struct ref_namespace_info ref_namespace[] = { [NAMESPACE_HEAD] = { .ref = "HEAD", @@ -550,13 +565,16 @@ void normalize_glob_ref(struct string_list_item *item, const char *prefix, if (prefix) strbuf_addstr(&normalized_pattern, prefix); - else if (!starts_with(pattern, "refs/") && - strcmp(pattern, "HEAD")) - strbuf_addstr(&normalized_pattern, "refs/"); - /* - * NEEDSWORK: Special case other symrefs such as REBASE_HEAD, - * MERGE_HEAD, etc. - */ + else if (!starts_with(pattern, "refs/") && strcmp(pattern, "HEAD")) { + int i; + + for (i = 0; i < ARRAY_SIZE(pseudorefs); i++) + if (!strcmp(pattern, pseudorefs[i])) + break; + + if (i == ARRAY_SIZE(pseudorefs)) + strbuf_addstr(&normalized_pattern, "refs/"); + } strbuf_addstr(&normalized_pattern, pattern); strbuf_strip_suffix(&normalized_pattern, "/"); @@ -1549,6 +1567,33 @@ int head_ref(each_ref_fn fn, void *cb_data) return refs_head_ref(get_main_ref_store(the_repository), fn, cb_data); } +int refs_for_each_pseudoref(struct ref_store *refs, + each_ref_fn fn, void *cb_data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pseudorefs); i++) { + struct object_id oid; + int flag; + + if (refs_resolve_ref_unsafe(refs, pseudorefs[i], + RESOLVE_REF_READING, &oid, &flag)) { + int ret = fn(pseudorefs[i], &oid, flag, cb_data); + + if (ret) + return ret; + } + } + + return 0; +} + +int for_each_pseudoref(each_ref_fn fn, void *cb_data) +{ + return refs_for_each_pseudoref(get_main_ref_store(the_repository), + fn, cb_data); +} + struct ref_iterator *refs_ref_iterator_begin( struct ref_store *refs, const char *prefix, diff --git a/refs.h b/refs.h index 23211a5ea1..7b55cced31 100644 --- a/refs.h +++ b/refs.h @@ -320,6 +320,8 @@ typedef int each_repo_ref_fn(struct repository *r, */ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data); +int refs_for_each_pseudoref(struct ref_store *refs, + each_ref_fn fn, void *cb_data); int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data); int refs_for_each_ref_in(struct ref_store *refs, const char *prefix, @@ -334,6 +336,9 @@ int refs_for_each_remote_ref(struct ref_store *refs, /* just iterates the head ref. */ int head_ref(each_ref_fn fn, void *cb_data); +/* iterates pseudorefs. */ +int for_each_pseudoref(each_ref_fn fn, void *cb_data); + /* iterates all refs. */ int for_each_ref(each_ref_fn fn, void *cb_data); diff --git a/t/t4013/diff.log_--decorate=full_--clear-decorations_--all b/t/t4013/diff.log_--decorate=full_--clear-decorations_--all index 1c030a6554..7d16978e7f 100644 --- a/t/t4013/diff.log_--decorate=full_--clear-decorations_--all +++ b/t/t4013/diff.log_--decorate=full_--clear-decorations_--all @@ -33,13 +33,13 @@ Date: Mon Jun 26 00:04:00 2006 +0000 Merge branch 'side' -commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side) +commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (FETCH_HEAD, refs/heads/side) Author: A U Thor Date: Mon Jun 26 00:03:00 2006 +0000 Side -commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 +commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 (ORIG_HEAD) Author: A U Thor Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4013/diff.log_--decorate_--clear-decorations_--all b/t/t4013/diff.log_--decorate_--clear-decorations_--all index 88be82cce3..4f9be50ce0 100644 --- a/t/t4013/diff.log_--decorate_--clear-decorations_--all +++ b/t/t4013/diff.log_--decorate_--clear-decorations_--all @@ -33,13 +33,13 @@ Date: Mon Jun 26 00:04:00 2006 +0000 Merge branch 'side' -commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (side) +commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (FETCH_HEAD, side) Author: A U Thor Date: Mon Jun 26 00:03:00 2006 +0000 Side -commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 +commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 (ORIG_HEAD) Author: A U Thor Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4202-log.sh b/t/t4202-log.sh index ddd205f98a..7076af2740 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -927,7 +927,7 @@ test_expect_success 'multiple decorate-refs' ' test_expect_success 'decorate-refs-exclude with glob' ' cat >expect.decorate <<-\EOF && Merge-tag-reach (HEAD -> main) - Merge-tags-octopus-a-and-octopus-b + Merge-tags-octopus-a-and-octopus-b (ORIG_HEAD) seventh (tag: seventh) octopus-b (tag: octopus-b) octopus-a (tag: octopus-a) @@ -944,7 +944,7 @@ test_expect_success 'decorate-refs-exclude with glob' ' test_expect_success 'decorate-refs-exclude without globs' ' cat >expect.decorate <<-\EOF && Merge-tag-reach (HEAD -> main) - Merge-tags-octopus-a-and-octopus-b + Merge-tags-octopus-a-and-octopus-b (ORIG_HEAD) seventh (tag: seventh) octopus-b (tag: octopus-b, octopus-b) octopus-a (tag: octopus-a, octopus-a) @@ -961,7 +961,7 @@ test_expect_success 'decorate-refs-exclude without globs' ' test_expect_success 'multiple decorate-refs-exclude' ' cat >expect.decorate <<-\EOF && Merge-tag-reach (HEAD -> main) - Merge-tags-octopus-a-and-octopus-b + Merge-tags-octopus-a-and-octopus-b (ORIG_HEAD) seventh (tag: seventh) octopus-b (tag: octopus-b) octopus-a (tag: octopus-a) @@ -1022,10 +1022,12 @@ test_expect_success 'decorate-refs-exclude and simplify-by-decoration' ' EOF git log -n6 --decorate=short --pretty="tformat:%f%d" \ --decorate-refs-exclude="*octopus*" \ + --decorate-refs-exclude="ORIG_HEAD" \ --simplify-by-decoration >actual && test_cmp expect.decorate actual && - git -c log.excludeDecoration="*octopus*" log \ - -n6 --decorate=short --pretty="tformat:%f%d" \ + git -c log.excludeDecoration="*octopus*" \ + -c log.excludeDecoration="ORIG_HEAD" \ + log -n6 --decorate=short --pretty="tformat:%f%d" \ --simplify-by-decoration >actual && test_cmp expect.decorate actual ' @@ -1067,9 +1069,10 @@ test_expect_success 'decorate-refs and simplify-by-decoration without output' ' test_cmp expect actual ' -test_expect_success 'decorate-refs-exclude HEAD' ' +test_expect_success 'decorate-refs-exclude HEAD ORIG_HEAD' ' git log --decorate=full --oneline \ - --decorate-refs-exclude="HEAD" >actual && + --decorate-refs-exclude="HEAD" \ + --decorate-refs-exclude="ORIG_HEAD" >actual && ! grep HEAD actual ' @@ -1107,7 +1110,7 @@ test_expect_success '--clear-decorations overrides defaults' ' cat >expect.all <<-\EOF && Merge-tag-reach (HEAD -> refs/heads/main) - Merge-tags-octopus-a-and-octopus-b + Merge-tags-octopus-a-and-octopus-b (ORIG_HEAD) seventh (tag: refs/tags/seventh) octopus-b (tag: refs/tags/octopus-b, refs/heads/octopus-b) octopus-a (tag: refs/tags/octopus-a, refs/heads/octopus-a) @@ -1139,7 +1142,7 @@ test_expect_success '--clear-decorations clears previous exclusions' ' cat >expect.all <<-\EOF && Merge-tag-reach (HEAD -> refs/heads/main) reach (tag: refs/tags/reach, refs/heads/reach) - Merge-tags-octopus-a-and-octopus-b + Merge-tags-octopus-a-and-octopus-b (ORIG_HEAD) octopus-b (tag: refs/tags/octopus-b, refs/heads/octopus-b) octopus-a (tag: refs/tags/octopus-a, refs/heads/octopus-a) seventh (tag: refs/tags/seventh) diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh index 73ea9e5155..9be3c523d4 100755 --- a/t/t4207-log-decoration-colors.sh +++ b/t/t4207-log-decoration-colors.sh @@ -17,7 +17,10 @@ test_expect_success setup ' git config color.decorate.remoteBranch red && git config color.decorate.tag "reverse bold yellow" && git config color.decorate.stash magenta && + git config color.decorate.ref blue && + git config color.decorate.pseudoref "bold cyan" && git config color.decorate.grafted black && + git config color.decorate.symbol white && git config color.decorate.HEAD cyan && c_reset="" && @@ -27,10 +30,14 @@ test_expect_success setup ' c_remoteBranch="" && c_tag="" && c_stash="" && + c_ref="" && + c_pseudoref="" && c_HEAD="" && c_grafted="" && + c_symbol="" && test_commit A && + git update-ref refs/foo A && git clone . other && ( cd other && @@ -41,7 +48,10 @@ test_expect_success setup ' test_commit B && git tag v1.0 && echo >>A.t && - git stash save Changes to A.t + git stash save Changes to A.t && + git reset other/main && + git reset ORIG_HEAD && + git revert --no-commit @~ ' cmp_filtered_decorations () { @@ -53,20 +63,24 @@ cmp_filtered_decorations () { # to this test since it does not contain any decoration, hence --first-parent test_expect_success 'commit decorations colored correctly' ' cat >expect <<-EOF && - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\ -${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \ -${c_reset}${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \ -${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B -${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ -${c_tag}tag: ${c_reset}${c_tag}A1${c_reset}${c_commit}, \ -${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1 - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ -${c_stash}refs/stash${c_reset}${c_commit})${c_reset} On main: Changes to A.t - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ -${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A + ${c_commit}COMMIT_ID${c_reset}${c_symbol} (${c_reset}${c_HEAD}HEAD${c_reset}\ +${c_symbol} -> ${c_reset}${c_branch}main${c_reset}${c_symbol}, ${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_symbol}, ${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_symbol})${c_reset} B +${c_commit}COMMIT_ID${c_reset}${c_symbol} (${c_reset}\ +${c_pseudoref}ORIG_HEAD${c_reset}${c_symbol}, ${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}A1${c_reset}${c_symbol}, ${c_reset}\ +${c_remoteBranch}other/main${c_reset}${c_symbol})${c_reset} A1 + ${c_commit}COMMIT_ID${c_reset}${c_symbol} (${c_reset}\ +${c_stash}refs/stash${c_reset}${c_symbol})${c_reset} On main: Changes to A.t + ${c_commit}COMMIT_ID${c_reset}${c_symbol} (${c_reset}\ +${c_pseudoref}REVERT_HEAD${c_reset}${c_symbol}, ${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_symbol}, ${c_reset}\ +${c_ref}refs/foo${c_reset}${c_symbol})${c_reset} A EOF - git log --first-parent --no-abbrev --decorate --oneline --color=always --all >actual && + git log --first-parent --no-abbrev --decorate --color=always \ + --decorate-refs-exclude=FETCH_HEAD --oneline --all >actual && cmp_filtered_decorations ' @@ -84,14 +98,14 @@ test_expect_success 'test coloring with replace-objects' ' git replace HEAD~1 HEAD~2 && cat >expect <<-EOF && - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\ -${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \ -${c_reset}${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_commit})${c_reset} D - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ -${c_tag}tag: ${c_reset}${c_tag}C${c_reset}${c_commit}, \ -${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} B - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ -${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A + ${c_commit}COMMIT_ID${c_reset}${c_symbol} (${c_reset}${c_HEAD}HEAD${c_reset}\ +${c_symbol} -> ${c_reset}${c_branch}main${c_reset}${c_symbol}, ${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_symbol})${c_reset} D + ${c_commit}COMMIT_ID${c_reset}${c_symbol} (${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}C${c_reset}${c_symbol}, ${c_reset}\ +${c_grafted}replaced${c_reset}${c_symbol})${c_reset} B + ${c_commit}COMMIT_ID${c_reset}${c_symbol} (${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_symbol})${c_reset} A EOF git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual && @@ -110,15 +124,15 @@ test_expect_success 'test coloring with grafted commit' ' git replace --graft HEAD HEAD~2 && cat >expect <<-EOF && - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\ -${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \ -${c_reset}${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_commit}, \ -${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} D - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ -${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \ -${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ -${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A + ${c_commit}COMMIT_ID${c_reset}${c_symbol} (${c_reset}${c_HEAD}HEAD${c_reset}\ +${c_symbol} -> ${c_reset}${c_branch}main${c_reset}${c_symbol}, ${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_symbol}, ${c_reset}\ +${c_grafted}replaced${c_reset}${c_symbol})${c_reset} D + ${c_commit}COMMIT_ID${c_reset}${c_symbol} (${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_symbol}, ${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_symbol})${c_reset} B + ${c_commit}COMMIT_ID${c_reset}${c_symbol} (${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_symbol})${c_reset} A EOF git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual &&