From: Dylan Reid <dgreid@gmail.com>
To: git@vger.kernel.org
Cc: Dylan Reid <dgreid@gmail.com>
Subject: [PATCH] blame: can specify shas of commits to ignore on command line
Date: Mon, 3 May 2010 22:21:27 -0400 [thread overview]
Message-ID: <1272939687-17686-1-git-send-email-dgreid@gmail.com> (raw)
Add the ability for git-blame to ignore changes that occur in
certain commits. The new "-I <sha>" argument can be used to specify
a commit that should be ignored. This is useful if you have a
few commits that you know didn't cause a problem, for example you
switched from "u8" to "uint8_t" and it messed up your history.
Allow multiple commits to be specified and store an array in the
scoreboard structure so it is accessible. Add should_ignore_commit
function to check if a commit should be ignored. Call
"should_ignore_commit" from blame_overlap and if the commit should
be ignored then pass all the blame on to the parent of the ignored
commit. This is done by calling "pass_whole_blame" which has been
relocated to a above blame_overlap, but is unchanged.
Signed-off-by: Dylan Reid <dgreid@gmail.com>
---
Documentation/blame-options.txt | 6 ++
builtin/blame.c | 124 +++++++++++++++++++++++++++++----------
t/t8003-blame.sh | 42 +++++++++++++
3 files changed, 141 insertions(+), 31 deletions(-)
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index d820569..eb9c825 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -52,6 +52,12 @@ of lines before or after the line given by <start>.
--porcelain::
Show in a format designed for machine consumption.
+--ignore-commit <sha>::
+ Ignore the specified commit when assigning blame. This is
+ useful if there are commits in the history that make non
+ functional changes to the lines you are interested in
+ finding blame for.
+
--incremental::
Show the result incrementally in a format designed for
machine consumption.
diff --git a/builtin/blame.c b/builtin/blame.c
index fc15863..e4bd095 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -176,6 +176,14 @@ struct blame_entry {
};
/*
+ * List of any commits to ignore
+ */
+struct ignore_commits {
+ unsigned char (*ignore_shas)[20];
+ unsigned num_ignore_shas;
+};
+
+/*
* The current state of the blame assignment.
*/
struct scoreboard {
@@ -198,6 +206,9 @@ struct scoreboard {
/* look-up a line in the final buffer */
int num_lines;
int *lineno;
+
+ /* List of the shas of commits that should be ignored */
+ struct ignore_commits *ignored_commits;
};
static inline int same_suspect(struct origin *a, struct origin *b)
@@ -653,6 +664,44 @@ static void decref_split(struct blame_entry *split)
}
/*
+ * The blobs of origin and porigin exactly match, so everything
+ * origin is suspected for can be blamed on the parent.
+ */
+static void pass_whole_blame(struct scoreboard *sb,
+ struct origin *origin, struct origin *porigin)
+{
+ struct blame_entry *e;
+
+ if (!porigin->file.ptr && origin->file.ptr) {
+ /* Steal its file */
+ porigin->file = origin->file;
+ origin->file.ptr = NULL;
+ }
+ for (e = sb->ent; e; e = e->next) {
+ if (!same_suspect(e->suspect, origin))
+ continue;
+ origin_incref(porigin);
+ origin_decref(e->suspect);
+ e->suspect = porigin;
+ }
+}
+
+/*
+ * Helper to determine if the given commit should be ignored
+ */
+static unsigned should_ignore_commit(const struct scoreboard *sb,
+ struct commit *commit)
+{
+ unsigned i;
+ unsigned num_shas = sb->ignored_commits->num_ignore_shas;
+ for(i = 0; i < num_shas; i++)
+ if(!hashcmp(commit->object.sha1,
+ sb->ignored_commits->ignore_shas[i]))
+ return 1;
+ return 0;
+}
+
+/*
* Helper for blame_chunk(). blame_entry e is known to overlap with
* the patch hunk; split it and pass blame to the parent.
*/
@@ -660,12 +709,15 @@ static void blame_overlap(struct scoreboard *sb, struct blame_entry *e,
int tlno, int plno, int same,
struct origin *parent)
{
- struct blame_entry split[3];
-
- split_overlap(split, e, tlno, plno, same, parent);
- if (split[1].suspect)
- split_blame(sb, split, e);
- decref_split(split);
+ if(should_ignore_commit(sb, e->suspect->commit))
+ pass_whole_blame(sb, e->suspect, parent);
+ else {
+ struct blame_entry split[3];
+ split_overlap(split, e, tlno, plno, same, parent);
+ if (split[1].suspect)
+ split_blame(sb, split, e);
+ decref_split(split);
+ }
}
/*
@@ -1105,29 +1157,6 @@ static int find_copy_in_parent(struct scoreboard *sb,
}
/*
- * The blobs of origin and porigin exactly match, so everything
- * origin is suspected for can be blamed on the parent.
- */
-static void pass_whole_blame(struct scoreboard *sb,
- struct origin *origin, struct origin *porigin)
-{
- struct blame_entry *e;
-
- if (!porigin->file.ptr && origin->file.ptr) {
- /* Steal its file */
- porigin->file = origin->file;
- origin->file.ptr = NULL;
- }
- for (e = sb->ent; e; e = e->next) {
- if (!same_suspect(e->suspect, origin))
- continue;
- origin_incref(porigin);
- origin_decref(e->suspect);
- e->suspect = porigin;
- }
-}
-
-/*
* We pass blame from the current commit to its parents. We keep saying
* "parent" (and "porigin"), but what we mean is to find scapegoat to
* exonerate ourselves.
@@ -1540,8 +1569,14 @@ static void assign_blame(struct scoreboard *sb, int opt)
/* Take responsibility for the remaining entries */
for (ent = sb->ent; ent; ent = ent->next)
- if (same_suspect(ent->suspect, suspect))
- found_guilty_entry(ent);
+ if (same_suspect(ent->suspect, suspect)) {
+ if (should_ignore_commit(sb, commit) &&
+ ent->suspect->previous)
+ pass_whole_blame(sb, ent->suspect,
+ ent->suspect->previous);
+ else
+ found_guilty_entry(ent);
+ }
origin_decref(suspect);
if (DEBUG) /* sanity */
@@ -2204,6 +2239,24 @@ static int blame_bottomtop_callback(const struct option *option, const char *arg
return 0;
}
+static int blame_ignorecommit_callback(const struct option *option,
+ const char *arg, int unset)
+{
+ struct ignore_commits *commits = option->value;
+ if (!arg)
+ return -1;
+ commits->ignore_shas =
+ realloc(commits->ignore_shas,
+ ( (commits->num_ignore_shas + 1) *
+ 20 ));
+ if(!get_sha1(arg, commits->ignore_shas[commits->num_ignore_shas]))
+ commits->num_ignore_shas++;
+ else
+ return -1;
+
+ return 0;
+}
+
int cmd_blame(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
@@ -2215,6 +2268,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
const char *final_commit_name = NULL;
enum object_type type;
+ static struct ignore_commits ignored_commits;
static const char *bottomtop = NULL;
static int output_option = 0, opt = 0;
static int show_stats = 0;
@@ -2239,6 +2293,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
{ OPTION_CALLBACK, 'C', NULL, &opt, "score", "Find line copies within and across files", PARSE_OPT_OPTARG, blame_copy_callback },
{ OPTION_CALLBACK, 'M', NULL, &opt, "score", "Find line movements within and across files", PARSE_OPT_OPTARG, blame_move_callback },
OPT_CALLBACK('L', NULL, &bottomtop, "n,m", "Process only line range n,m, counting from 1", blame_bottomtop_callback),
+ OPT_CALLBACK(0, "ignore-commit", &ignored_commits, "sha", "don't blame the specified commit for anything", blame_ignorecommit_callback),
OPT_END()
};
@@ -2252,6 +2307,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
save_commit_buffer = 0;
dashdash_pos = 0;
+ memset(&ignored_commits, 0, sizeof(ignored_commits));
+
parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |
PARSE_OPT_KEEP_ARGV0);
for (;;) {
@@ -2369,6 +2426,8 @@ parse_done:
setup_revisions(argc, argv, &revs, NULL);
memset(&sb, 0, sizeof(sb));
+ sb.ignored_commits = &ignored_commits;
+
sb.revs = &revs;
if (!reverse)
final_commit_name = prepare_final(&sb);
@@ -2468,6 +2527,9 @@ parse_done:
ent = e;
}
+ if (ignored_commits.ignore_shas)
+ free(ignored_commits.ignore_shas);
+
if (show_stats) {
printf("num read blob: %d\n", num_read_blob);
printf("num get patch: %d\n", num_get_patch);
diff --git a/t/t8003-blame.sh b/t/t8003-blame.sh
index 230143c..f90d37a 100755
--- a/t/t8003-blame.sh
+++ b/t/t8003-blame.sh
@@ -185,4 +185,46 @@ test_expect_success 'indent of line numbers, ten lines' '
test $(grep -c " " actual) = 9
'
+test_expect_success 'blame ignore setup with split' '
+ echo A A A >one
+ echo b b b b b >> one
+ echo c c c c c >> one
+ git add one
+ GIT_AUTHOR_NAME=AddLine git commit -m "add As"
+ INIT_ADD_SHA=`git log --pretty=format:"%H" HEAD^..`
+ echo " " A A A >one
+ echo b b b b b >> one
+ echo c c c c c >> one
+ git add one
+ GIT_AUTHOR_NAME=Indent git commit -m "reindent"
+ IGNORE_SHA=`git log --pretty=format:"%H" HEAD^..`
+'
+
+test_expect_success 'blame ignore commit' '
+ git blame --ignore-commit $IGNORE_SHA one > ignored &&
+ test $(grep -c "AddLine" ignored) = 3
+'
+
+test_expect_success 'blame ignore first commit' '
+ git blame --ignore-commit $IGNORE_SHA --ignore-commit $INIT_ADD_SHA \
+ $INIT_ADD_SHA.. -- one > ignore_1st &&
+ test $(grep -c "AddLine" ignore_1st) = 3
+'
+
+test_expect_success 'blame ignore setup avoid blame_overlap' '
+ echo A A A >one
+ git add one
+ GIT_AUTHOR_NAME=AddLine git commit -m "add As"
+ INIT_ADD_SHA=`git log --pretty=format:"%H" HEAD^..`
+ echo " " A A A >one
+ git add one
+ GIT_AUTHOR_NAME=Indent git commit -m "reindent"
+ IGNORE_SHA=`git log --pretty=format:"%H" HEAD^..`
+'
+
+test_expect_success 'blame ignore commit no blame_overlap call' '
+ git blame --ignore-commit $IGNORE_SHA one > ignored &&
+ test $(grep -c "Indent" ignored) = 0
+'
+
test_done
--
1.7.1.6.gc7a9
next reply other threads:[~2010-05-04 2:25 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-05-04 2:21 Dylan Reid [this message]
2010-05-04 21:28 ` [PATCH] blame: can specify shas of commits to ignore on command line René Scharfe
2010-05-04 21:46 ` Dylan Reid
2010-05-04 23:01 ` Junio C Hamano
2010-05-04 23:08 ` Dylan Reid
2010-05-05 5:19 ` Junio C Hamano
2010-05-05 5:28 ` Dylan Reid
2012-03-08 21:01 ` cnighswonger
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
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1272939687-17686-1-git-send-email-dgreid@gmail.com \
--to=dgreid@gmail.com \
--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.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).