Introduce a new revision set operator <rev>^!
authorJunio C Hamano <junkio@cox.net>
Tue, 31 Oct 2006 22:22:34 +0000 (14:22 -0800)
committerJunio C Hamano <junkio@cox.net>
Tue, 31 Oct 2006 22:22:34 +0000 (14:22 -0800)
This is a shorthand for "<rev> --not <rev>^@", i.e. "include
this commit but exclude any of its parents".

When a new file $F is introduced by revision $R, this notation
can be used to find a copy-and-paste from existing file in the
parents of that revision without annotating the ancestry of the
lines that were copied from:

git pickaxe -f -C $R^! -- $F

Signed-off-by: Junio C Hamano <junkio@cox.net>
Documentation/git-pickaxe.txt
Documentation/git-rev-parse.txt
revision.c

index 6d22fd9e993edebf229c9e35b01fd2ecf6f59de5..c08fdec191157f3d2916cd83906aeba5993c07ba 100644 (file)
@@ -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]
index 5d4257062d1776ee18900b84e805197a9028e2f0..cda80b18f45c51b038838a62d0707172f2f273c9 100644 (file)
@@ -222,14 +222,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
 ------
index f1e0caaae3d2a96de6cf9bad12e8641c19d788dd..b021d3354e8b0f2f1efb6dbda0aa1289914eb112 100644 (file)
@@ -660,6 +660,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;