All of lore.kernel.org
 help / color / mirror / Atom feed
From: "David Barr via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: "Ævar Arnfjörð Bjarmason" <avarab@gmail.com>,
	"David Barr" <git@davebarr.dev>, "David Barr" <git@davebarr.dev>
Subject: [PATCH] blame: add --ignore-revs-blob and blame.ignoreRevsBlob
Date: Sat, 22 Jan 2022 10:07:12 +0000	[thread overview]
Message-ID: <pull.1204.git.git.1642846032807.gitgitgadget@gmail.com> (raw)

From: David Barr <git@davebarr.dev>

In a bare repository, there isn't a simple way to
ignore revisions via a file without extracting it
to a temporary file.

This patch provides a command-line option and
config variable, similar to --ignore-revs-file
and blame.ignoreRevsFile, which reads the list
of revisions to ignore from a blob in the
repository.

Signed-off-by: David Barr <git@davebarr.dev>
---
    blame: Add --ignore-revs-blob and blame.ignoreRevsBlob
    
    In a bare repository, there isn't a simple way to ignore revisions via a
    file without extracting it to a temporary file.
    
    This patch provides a command-line option and config variable, similar
    to --ignore-revs-file and blame.ignoreRevsFile, which reads the list of
    revisions to ignore from a blob in the repository.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1204%2Fdavebarrau%2Fadd-ignore-revs-blob-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1204/davebarrau/add-ignore-revs-blob-v1
Pull-Request: https://github.com/git/git/pull/1204

 Documentation/blame-options.txt | 10 ++--
 Documentation/config/blame.txt  |  7 ++-
 builtin/blame.c                 | 23 ++++++++-
 oidset.c                        | 84 ++++++++++++++++++++++++++-------
 oidset.h                        |  2 +
 t/t8013-blame-ignore-revs.sh    | 19 ++++++++
 6 files changed, 124 insertions(+), 21 deletions(-)

diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index 9a663535f44..d6e59c57983 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -132,9 +132,13 @@ take effect.
 --ignore-revs-file <file>::
 	Ignore revisions listed in `file`, which must be in the same format as an
 	`fsck.skipList`.  This option may be repeated, and these files will be
-	processed after any files specified with the `blame.ignoreRevsFile` config
-	option.  An empty file name, `""`, will clear the list of revs from
-	previously processed files.
+	processed after any files specified with the `blame.ignoreRevsFile` or
+	`blame.ignoreRevsBlob` config options.  An empty file name, `""`, will
+	clear the list of revs from previously processed files.
+
+--ignore-revs-blob <blob>::
+	Like `--ignore-revs-file`, but consider the value as a reference to a blob
+	in the repository.
 
 --color-lines::
 	Color line annotations in the default format differently if they come from
diff --git a/Documentation/config/blame.txt b/Documentation/config/blame.txt
index 4d047c17908..109ca796de0 100644
--- a/Documentation/config/blame.txt
+++ b/Documentation/config/blame.txt
@@ -25,7 +25,12 @@ blame.ignoreRevsFile::
 	line, in linkgit:git-blame[1].  Whitespace and comments beginning with
 	`#` are ignored.  This option may be repeated multiple times.  Empty
 	file names will reset the list of ignored revisions.  This option will
-	be handled before the command line option `--ignore-revs-file`.
+	be handled before the command line options `--ignore-revs-file` and
+	`--ignore-revs-blob`.
+
+blame.ignoreRevsBlob::
+	Like `blame.ignoreRevsFile`, but consider the value as a reference to
+	a blob in the repository.
 
 blame.markUnblamableLines::
 	Mark lines that were changed by an ignored revision that we could not
diff --git a/builtin/blame.c b/builtin/blame.c
index 7fafeac4081..c70c99cfda5 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -54,6 +54,7 @@ static int show_progress;
 static char repeated_meta_color[COLOR_MAXLEN];
 static int coloring_mode;
 static struct string_list ignore_revs_file_list = STRING_LIST_INIT_NODUP;
+static struct string_list ignore_revs_blob_list = STRING_LIST_INIT_NODUP;
 static int mark_unblamable_lines;
 static int mark_ignored_lines;
 
@@ -711,6 +712,16 @@ static int git_blame_config(const char *var, const char *value, void *cb)
 		string_list_insert(&ignore_revs_file_list, str);
 		return 0;
 	}
+	if (!strcmp(var, "blame.ignorerevsblob")) {
+		const char *str;
+		int ret;
+
+		ret = git_config_string(&str, var, value);
+		if (ret)
+			return ret;
+		string_list_insert(&ignore_revs_blob_list, str);
+		return 0;
+	}
 	if (!strcmp(var, "blame.markunblamablelines")) {
 		mark_unblamable_lines = git_config_bool(var, value);
 		return 0;
@@ -822,6 +833,7 @@ static int peel_to_commit_oid(struct object_id *oid_ret, void *cbdata)
 
 static void build_ignorelist(struct blame_scoreboard *sb,
 			     struct string_list *ignore_revs_file_list,
+			     struct string_list *ignore_revs_blob_list,
 			     struct string_list *ignore_rev_list)
 {
 	struct string_list_item *i;
@@ -835,6 +847,13 @@ static void build_ignorelist(struct blame_scoreboard *sb,
 			oidset_parse_file_carefully(&sb->ignore_list, i->string,
 						    peel_to_commit_oid, sb);
 	}
+	for_each_string_list_item(i, ignore_revs_blob_list) {
+		if (!strcmp(i->string, ""))
+			oidset_clear(&sb->ignore_list);
+		else
+			oidset_parse_blob(&sb->ignore_list, i->string,
+						    peel_to_commit_oid, sb);
+	}
 	for_each_string_list_item(i, ignore_rev_list) {
 		if (get_oid_committish(i->string, &oid) ||
 		    peel_to_commit_oid(&oid, sb))
@@ -878,6 +897,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 		OPT_BIT('w', NULL, &xdl_opts, N_("ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
 		OPT_STRING_LIST(0, "ignore-rev", &ignore_rev_list, N_("rev"), N_("ignore <rev> when blaming")),
 		OPT_STRING_LIST(0, "ignore-revs-file", &ignore_revs_file_list, N_("file"), N_("ignore revisions from <file>")),
+		OPT_STRING_LIST(0, "ignore-revs-blob", &ignore_revs_blob_list, N_("blob"), N_("ignore revisions from <blob>")),
 		OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE),
 		OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR),
 		OPT_BIT(0, "minimal", &xdl_opts, N_("spend extra cycles to find better match"), XDF_NEED_MINIMAL),
@@ -1084,8 +1104,9 @@ parse_done:
 	sb.reverse = reverse;
 	sb.repo = the_repository;
 	sb.path = path;
-	build_ignorelist(&sb, &ignore_revs_file_list, &ignore_rev_list);
+	build_ignorelist(&sb, &ignore_revs_file_list, &ignore_revs_blob_list, &ignore_rev_list);
 	string_list_clear(&ignore_revs_file_list, 0);
+	string_list_clear(&ignore_revs_blob_list, 0);
 	string_list_clear(&ignore_rev_list, 0);
 	setup_scoreboard(&sb, &o);
 
diff --git a/oidset.c b/oidset.c
index b36a2bae864..0cca63700da 100644
--- a/oidset.c
+++ b/oidset.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "oidset.h"
+#include "object-store.h"
 
 void oidset_init(struct oidset *set, size_t initial_size)
 {
@@ -41,6 +42,29 @@ void oidset_parse_file(struct oidset *set, const char *path)
 	oidset_parse_file_carefully(set, path, NULL, NULL);
 }
 
+static int read_oidset_line(struct strbuf sb, struct object_id *oid)
+{
+       const char *p;
+       const char *name;
+
+       /*
+	* Allow trailing comments, leading whitespace
+	* (including before commits), and empty or whitespace
+	* only lines.
+	*/
+       name = strchr(sb.buf, '#');
+       if (name)
+	       strbuf_setlen(&sb, name - sb.buf);
+       strbuf_trim(&sb);
+       if (!sb.len)
+	       return 0;
+
+       if (parse_oid_hex(sb.buf, oid, &p) || *p != '\0')
+	       die("invalid object name: %s", sb.buf);
+
+       return 1;
+}
+
 void oidset_parse_file_carefully(struct oidset *set, const char *path,
 				 oidset_parse_tweak_fn fn, void *cbdata)
 {
@@ -52,23 +76,8 @@ void oidset_parse_file_carefully(struct oidset *set, const char *path,
 	if (!fp)
 		die("could not open object name list: %s", path);
 	while (!strbuf_getline(&sb, fp)) {
-		const char *p;
-		const char *name;
-
-		/*
-		 * Allow trailing comments, leading whitespace
-		 * (including before commits), and empty or whitespace
-		 * only lines.
-		 */
-		name = strchr(sb.buf, '#');
-		if (name)
-			strbuf_setlen(&sb, name - sb.buf);
-		strbuf_trim(&sb);
-		if (!sb.len)
+		if (!read_oidset_line(sb, &oid))
 			continue;
-
-		if (parse_oid_hex(sb.buf, &oid, &p) || *p != '\0')
-			die("invalid object name: %s", sb.buf);
 		if (fn && fn(&oid, cbdata))
 			continue;
 		oidset_insert(set, &oid);
@@ -78,3 +87,46 @@ void oidset_parse_file_carefully(struct oidset *set, const char *path,
 	fclose(fp);
 	strbuf_release(&sb);
 }
+
+static void read_oidset_string(struct oidset *set, oidset_parse_tweak_fn fn,
+			       void *cbdata, const char *buf, unsigned long size)
+{
+	struct object_id oid;
+	struct strbuf **lines;
+	struct strbuf **line;
+
+	lines = strbuf_split_buf(buf, size, '\n', 0);
+
+	for (line = lines; *line; line++) {
+		if (!read_oidset_line(**line, &oid))
+			continue;
+		if (fn && fn(&oid, cbdata))
+			continue;
+		oidset_insert(set, &oid);
+	}
+	strbuf_list_free(lines);
+}
+
+void oidset_parse_blob(struct oidset *set, const char *name,
+				 oidset_parse_tweak_fn fn, void *cbdata)
+{
+	struct object_id oid;
+	char *buf;
+	unsigned long size;
+	enum object_type type;
+
+	if (!name) {
+		return;
+	}
+	if (get_oid(name, &oid) < 0) {
+		die("unable to read object id for %s", name);
+	}
+	buf = read_object_file(&oid, &type, &size);
+	if (!buf)
+		die("unable to read oidset file at %s", name);
+	if (type != OBJ_BLOB)
+		die("oidset file is not a blob: %s", name);
+
+	read_oidset_string(set, fn, cbdata, buf, size);
+	free(buf);
+}
diff --git a/oidset.h b/oidset.h
index ba4a5a2cd3a..bad9246a150 100644
--- a/oidset.h
+++ b/oidset.h
@@ -84,6 +84,8 @@ void oidset_parse_file(struct oidset *set, const char *path);
 typedef int (*oidset_parse_tweak_fn)(struct object_id *, void *);
 void oidset_parse_file_carefully(struct oidset *set, const char *path,
 				 oidset_parse_tweak_fn fn, void *cbdata);
+void oidset_parse_blob(struct oidset *set, const char *path,
+				 oidset_parse_tweak_fn fn, void *cbdata);
 
 struct oidset_iter {
 	kh_oid_set_t *set;
diff --git a/t/t8013-blame-ignore-revs.sh b/t/t8013-blame-ignore-revs.sh
index b18633dee1b..9d38a473a39 100755
--- a/t/t8013-blame-ignore-revs.sh
+++ b/t/t8013-blame-ignore-revs.sh
@@ -115,6 +115,25 @@ test_expect_success ignore_revs_from_configs_and_files '
 	test_cmp expect actual
 '
 
+# Ignore X from the config option, Y from a file.
+test_expect_success ignore_revs_from_configs_and_blobs '
+	git rev-parse X >ignore_x &&
+	git rev-parse Y >ignore_y &&
+	git add ignore_x ignore_y &&
+	git commit -m ignore &&
+	git config --add blame.ignoreRevsBlob HEAD:ignore_x &&
+	git blame --line-porcelain file --ignore-revs-blob HEAD:ignore_y >blame_raw 2>&1 &&
+	git config --unset blame.ignoreRevsBlob HEAD:ignore_x &&
+
+	grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+	git rev-parse A >expect &&
+	test_cmp expect actual &&
+
+	grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+	git rev-parse B >expect &&
+	test_cmp expect actual
+'
+
 # Override blame.ignoreRevsFile (ignore_x) with an empty string.  X should be
 # blamed now for lines 1 and 2, since we are no longer ignoring X.
 test_expect_success override_ignore_revs_file '

base-commit: 297ca895a27a6bbdb7906371d533f72a12ad25b2
-- 
gitgitgadget

             reply	other threads:[~2022-01-22 10:07 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-01-22 10:07 David Barr via GitGitGadget [this message]
2022-02-17  8:27 ` [PATCH] blame: add --ignore-revs-blob and blame.ignoreRevsBlob David Barr

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=pull.1204.git.git.1642846032807.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=avarab@gmail.com \
    --cc=git@davebarr.dev \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.