[PATCH 3/4] --decorate now decorates ancestors, too

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The option --decorate changed default behavior: Earlier, it decorated
commits pointed to by any ref.  The new behavior is this: decorate the
with the given refs and its ancestors, i.e.

	git log --decorate next master

will show "next", "next^", "next~2", ..., "master", "master^", ...
in parenthesis after the commit name.

However, you can still get the old behavior by giving --decorate=<mode>.
The available modes are:

- 'given'	the new default behavior,
- 'given-refs'	only decorate with the given refs, not their ancestors,
- 'tag'		decorate with all tag refs and their ancestors,
- 'tag-refs'	decorate with all tag refs, but not their ancestors,
- 'any'		decorate with all refs and their ancestors,
- 'any-ref'	decorate only with the refs, not their ancestors

The old behavior was 'any-ref'.

NOTE: Calling "git log --decorate=tag <commit>" is _not_ the same as
calling "git log <commit> | git -p name-rev --stdin":

For one, the latter will only try to name 40-character hex sequences
(which the former does not care about), but then it will also try
to name such sequences in the commit messages and the diffs.

More importantly, though, "--decorate=tag" will only build the names
while traversing commits, which makes it faster, but will fail to
correctly identify something like "git log --decorate=tag v0.99~2".

Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx>
---
 Documentation/pretty-options.txt |   17 ++++++++--
 commit.c                         |   68 +++++++++++++++++++++++++++++++++++--
 commit.h                         |    4 ++-
 log-tree.c                       |    4 ++
 revision.c                       |   43 +++++++++++++++++++++++-
 revision.h                       |    9 +++++
 6 files changed, 136 insertions(+), 9 deletions(-)

diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index 3b21e0d..f82b1e9 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -21,6 +21,17 @@ people using 80-column terminals.
 	preferred by the user.  For non plumbing commands this
 	defaults to UTF-8.
 
---decorate::
-	When a commit is shown, and it matches a ref, print that ref name
-	in brackets after the commit name.
+--decorate[=<mode>]::
+	When a commit is shown, decorate its commit name with the refs
+	specified on the command line or their ancestors which point to
+	that commit.
++
+You can influence the behaviour by givin a mode:
+- 'given' is the default mode,
+- 'given-refs' skips the decoration for ancestors,
+- 'tag-refs' decorates only the tagged commits,
+- 'tag' decorates tagged commits and their ancestors,
+- 'any-ref' means that those commits are decorated that match any ref
+  exactly, but no ancestors, and
+- 'any' means that all commits that are pointed at by _any_ reachable
+  commit are decorated by the appropriate names.
diff --git a/commit.c b/commit.c
index c0748ed..3aa21cc 100644
--- a/commit.c
+++ b/commit.c
@@ -1554,13 +1554,24 @@ int in_merge_bases(struct commit *commit, struct commit **reference, int num)
 	return ret;
 }
 
-void add_name_decoration(const char *prefix, const char *name, struct object *obj)
+void add_name_decoration(const char *prefix, const char *name, int generation, struct object *obj)
 {
 	int plen = strlen(prefix);
 	int nlen = strlen(name);
-	struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + plen + nlen);
+	struct name_decoration *res, *existing;
+
+	res = xmalloc(sizeof(struct name_decoration) + plen + nlen);
 	memcpy(res->name, prefix, plen);
 	memcpy(res->name + plen, name, nlen + 1);
+
+	for (existing = lookup_decoration(&name_decoration, obj);
+			existing; existing = existing->next)
+		if (!strcmp(existing->name, res->name)) {
+			free(res);
+			return;
+		}
+
+	res->generation = generation;
 	res->next = add_decoration(&name_decoration, obj, res);
 }
 
@@ -1569,12 +1580,61 @@ int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags
 	struct object *obj = parse_object(sha1);
 	if (!obj)
 		return 0;
-	add_name_decoration("", refname, obj);
+	if (!prefixcmp(refname, "refs/heads/"))
+		refname += 11;
+	else if (!prefixcmp(refname, "refs/tags/"))
+		refname += 10;
+	else if (!prefixcmp(refname, "refs/remotes/"))
+		refname += 13;
+	else if (!prefixcmp(refname, "refs/"))
+		refname += 5;
+	add_name_decoration("", refname, 0, obj);
 	while (obj->type == OBJ_TAG) {
 		obj = ((struct tag *)obj)->tagged;
 		if (!obj)
 			break;
-		add_name_decoration("tag: ", refname, obj);
+		add_name_decoration("tag: ", refname, 0, obj);
 	}
 	return 0;
 }
+
+void add_name_decoration_to_parents(struct commit *commit)
+{
+	struct name_decoration *decoration, *decor;
+	struct commit_list *parents;
+	int parent_nr;
+
+	parents = commit->parents;
+	if (!parents)
+		return;
+
+	decoration = lookup_decoration(&name_decoration, &commit->object);
+	if (!decoration)
+		return;
+
+	for (decor = decoration; decor; decor = decor->next)
+		add_name_decoration("", decor->name, decor->generation + 1,
+				&parents->item->object);
+
+	parent_nr = 1;
+	while (parents->next) {
+		char buffer[PATH_MAX];
+		parents = parents->next;
+		parent_nr++;
+		for (decor = decoration; decor; decor = decor->next) {
+			if (decor->generation < 2)
+				snprintf(buffer, sizeof(buffer),
+						"%s%s^%d", decor->name,
+						decor->generation ? "^" : "",
+						parent_nr);
+			else
+				snprintf(buffer, sizeof(buffer),
+						"%s~%d^%d", decor->name,
+						decor->generation, parent_nr);
+			add_name_decoration("", buffer, 0,
+					&parents->item->object);
+		}
+	}
+}
+
+
diff --git a/commit.h b/commit.h
index 787ae5e..8dafc7b 100644
--- a/commit.h
+++ b/commit.h
@@ -26,11 +26,13 @@ extern const char *commit_type;
 extern struct decoration name_decoration;
 struct name_decoration {
 	struct name_decoration *next;
+	int generation;
 	char name[1];
 };
 
-void add_name_decoration(const char *prefix, const char *name, struct object *obj);
+void add_name_decoration(const char *prefix, const char *name, int generation, struct object *obj);
 int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
+void add_name_decoration_to_parents(struct commit *commit);
 
 struct commit *lookup_commit(const unsigned char *sha1);
 struct commit *lookup_commit_reference(const unsigned char *sha1);
diff --git a/log-tree.c b/log-tree.c
index 8624d5a..eb7641a 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -26,6 +26,10 @@ static void show_decorations(struct commit *commit)
 	prefix = " (";
 	while (decoration) {
 		printf("%s%s", prefix, decoration->name);
+		if (decoration->generation == 1)
+			putchar('^');
+		else if (decoration->generation > 1)
+			printf("~%d", decoration->generation);
 		prefix = ", ";
 		decoration = decoration->next;
 	}
diff --git a/revision.c b/revision.c
index 7683d0a..79880b4 100644
--- a/revision.c
+++ b/revision.c
@@ -641,6 +641,10 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
 	if (it->type != OBJ_COMMIT)
 		return 0;
 	commit = (struct commit *)it;
+	if (revs->decorate & DECORATE_GIVEN_REFS)
+		add_name_decoration("", arg, 0, it);
+	if (revs->decorate & DECORATE_RECURSIVE)
+		add_name_decoration_to_parents(commit);
 	for (parents = commit->parents; parents; parents = parents->next) {
 		it = &parents->item->object;
 		it->flags |= flags;
@@ -778,6 +782,11 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
 				verify_non_filename(revs->prefix, arg);
 			}
 
+			if (revs->decorate & DECORATE_GIVEN_REFS) {
+				add_name_decoration("", this, 0, &a->object);
+				add_name_decoration("", next, 0, &b->object);
+			}
+
 			if (symmetric) {
 				exclude = get_merge_bases(a, b, 1);
 				add_pending_commit_list(revs, exclude,
@@ -817,6 +826,8 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
 	if (!cant_be_filename)
 		verify_non_filename(revs->prefix, arg);
 	object = get_reference(revs, arg, sha1, flags ^ local_flags);
+	if (revs->decorate & DECORATE_GIVEN_REFS)
+		add_name_decoration("", arg, 0, object);
 	add_pending_object_with_mode(revs, object, arg, mode);
 	return 0;
 }
@@ -1177,7 +1188,29 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 				continue;
 			}
 			if (!strcmp(arg, "--decorate")) {
-				for_each_ref(add_ref_decoration, NULL);
+				revs->decorate = DECORATE_GIVEN_REFS |
+					DECORATE_RECURSIVE;
+				continue;
+			}
+			if (!prefixcmp(arg, "--decorate=")) {
+				if (!strcmp(arg + 11, "any-ref"))
+					revs->decorate = DECORATE_ALL_REFS;
+				else if (!strcmp(arg + 11, "given-refs"))
+					revs->decorate = DECORATE_GIVEN_REFS;
+				else if (!strcmp(arg + 11, "given"))
+					revs->decorate = DECORATE_GIVEN_REFS |
+						DECORATE_RECURSIVE;
+				else if (!strcmp(arg + 11, "any"))
+					revs->decorate = DECORATE_GIVEN_REFS |
+						DECORATE_ALL_REFS |
+						DECORATE_RECURSIVE;
+				else if (!strcmp(arg + 11, "tag-refs"))
+					revs->decorate = DECORATE_TAG_REFS;
+				else if (!strcmp(arg + 11, "tag"))
+					revs->decorate = DECORATE_TAG_REFS |
+						DECORATE_RECURSIVE;
+				else
+					die("Unknown argument: %s", arg);
 				continue;
 			}
 
@@ -1213,6 +1246,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 		}
 	}
 
+	if (revs->decorate & DECORATE_ALL_REFS)
+		for_each_ref(add_ref_decoration, NULL);
+	if (revs->decorate & DECORATE_TAG_REFS)
+		for_each_tag_ref(add_ref_decoration, NULL);
+
 	if (revs->grep_filter)
 		revs->grep_filter->regflags |= regflags;
 
@@ -1372,6 +1410,9 @@ static struct commit *get_revision_1(struct rev_info *revs)
 		revs->commits = entry->next;
 		free(entry);
 
+		if (revs->decorate & DECORATE_RECURSIVE)
+			add_name_decoration_to_parents(commit);
+
 		if (revs->reflog_info)
 			fake_reflog_parent(revs->reflog_info, commit);
 
diff --git a/revision.h b/revision.h
index f46b4d5..0c3dc67 100644
--- a/revision.h
+++ b/revision.h
@@ -16,6 +16,14 @@ struct log_info;
 
 typedef void (prune_fn_t)(struct rev_info *revs, struct commit *commit);
 
+enum decorate {
+	DECORATE_NONE = 0,
+	DECORATE_RECURSIVE = 1,
+	DECORATE_ALL_REFS = 2,
+	DECORATE_TAG_REFS = 4,
+	DECORATE_GIVEN_REFS = 8,
+};
+
 struct rev_info {
 	/* Starting list */
 	struct commit_list *commits;
@@ -65,6 +73,7 @@ struct rev_info {
 	unsigned int	shown_one:1,
 			abbrev_commit:1;
 	enum date_mode date_mode;
+	enum decorate	decorate;
 
 	const char **ignore_packed; /* pretend objects in these are unpacked */
 	int num_ignore_packed;
-- 
1.5.3.rc0.2783.gf3f7


-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux