diff --git a/Documentation/git-pickaxe.txt b/Documentation/git-pickaxe.txt index 6d22fd9e99..c08fdec191 100644 --- a/Documentation/git-pickaxe.txt +++ b/Documentation/git-pickaxe.txt @@ -111,6 +111,44 @@ The contents of the actual line is output after the above header, prefixed by a TAB. This is to allow adding more header elements later. + +SPECIFIYING RANGES +------------------ + +Unlike `git-blame` and `git-annotate` in older git, the extent +of annotation can be limited to both line ranges and revision +ranges. When you are interested in finding the origin for +ll. 40-60 for file `foo`, you can use `-L` option like this: + + git pickaxe -L 40,60 foo + +When you are not interested in changes older than the version +v2.6.18, or changes older than 3 weeks, you can use revision +range specifiers similar to `git-rev-list`: + + git pickaxe v2.6.18.. -- foo + git pickaxe --since=3.weeks -- foo + +When revision range specifiers are used to limit the annotation, +lines that have not changed since the range boundary (either the +commit v2.6.18 or the most recent commit that is more than 3 +weeks old in the above example) are blamed for that range +boundary commit. + +A particularly useful way is to see if an added file have lines +created by copy-and-paste from existing files. Sometimes this +indicates that the developer was being sloppy and did not +refactor the code properly. You can first find the commit that +introduced the file with: + + git log --diff-filter=A --pretty=short -- foo + +and then annotate the change between the commit and its +parents, using `commit{caret}!` notation: + + git pickaxe -C -C -f $commit^! -- foo + + SEE ALSO -------- gitlink:git-blame[1] diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index ed938aafb0..4eaf5a0d1e 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -239,14 +239,21 @@ of `r1` and `r2` and is defined as It it the set of commits that are reachable from either one of `r1` or `r2` but not from both. -Here are a few examples: +Two other shorthands for naming a set that is formed by a commit +and its parent commits exists. `r1{caret}@` notation means all +parents of `r1`. `r1{caret}!` includes commit `r1` but excludes +its all parents. + +Here are a handful examples: D A B D D F A B C D F - ^A G B D + ^A G B D ^A F B C F G...I C D F G I - ^B G I C D F G I + ^B G I C D F G I + F^@ A B C + F^! H D F H Author ------ diff --git a/builtin-pickaxe.c b/builtin-pickaxe.c index 32dc3932ed..c9405e918b 100644 --- a/builtin-pickaxe.c +++ b/builtin-pickaxe.c @@ -141,6 +141,8 @@ static int cmp_suspect(struct origin *a, struct origin *b) return strcmp(a->path, b->path); } +#define cmp_suspect(a, b) ( ((a)==(b)) ? 0 : cmp_suspect(a,b) ) + static void sanity_check_refcnt(struct scoreboard *); static void coalesce(struct scoreboard *sb) @@ -213,6 +215,12 @@ static struct origin *find_origin(struct scoreboard *sb, struct diff_options diff_opts; const char *paths[2]; + if (parent->util) { + struct origin *cached = parent->util; + if (!strcmp(cached->path, origin->path)) + return origin_incref(cached); + } + /* See if the origin->path is different between parent * and origin first. Most of the time they are the * same and diff-tree is fairly efficient about this. @@ -259,6 +267,12 @@ static struct origin *find_origin(struct scoreboard *sb, } } diff_flush(&diff_opts); + if (porigin) { + origin_incref(porigin); + if (parent->util) + origin_decref(parent->util); + parent->util = porigin; + } return porigin; } @@ -905,7 +919,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) continue; if (parse_commit(p)) continue; - porigin = find(sb, parent->item, origin); + porigin = find(sb, p, origin); if (!porigin) continue; if (!hashcmp(porigin->blob_sha1, origin->blob_sha1)) { @@ -1371,8 +1385,10 @@ static void sanity_check_refcnt(struct scoreboard *sb) ent->suspect->refcnt = -ent->suspect->refcnt; } for (ent = sb->ent; ent; ent = ent->next) { - /* then pick each and see if they have the the - * correct refcnt + /* then pick each and see if they have the the correct + * refcnt; note that ->util caching means origin's refcnt + * may well be greater than the number of blame entries + * that use it. */ int found; struct blame_entry *e; @@ -1386,7 +1402,7 @@ static void sanity_check_refcnt(struct scoreboard *sb) continue; found++; } - if (suspect->refcnt != found) + if (suspect->refcnt < found) baa = 1; } if (baa) { diff --git a/revision.c b/revision.c index 3dbc26c49c..993bb668a2 100644 --- a/revision.c +++ b/revision.c @@ -657,6 +657,13 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, return 0; *dotdot = '^'; } + dotdot = strstr(arg, "^!"); + if (dotdot && !dotdot[2]) { + *dotdot = 0; + if (!add_parents_only(revs, arg, flags ^ UNINTERESTING)) + *dotdot = '^'; + } + local_flags = 0; if (*arg == '^') { local_flags = UNINTERESTING;