Search code examples
gitgit-commitgit-tag

How to list all tags containing a commit's changes?


Suppose I have 2 tags X and Y such that

tag X log history: C1 C2 ... Cn
tag Y log history: Z1 Z2 ... Zm

If hash(Ci) = hash(Zj) (for a given i and j) and I want to know all tags having commit Ci I can just do, as answered in 'How to list all tags that contain a commit?',

git tag --contains Ci

and that would give me X and Y.

Is it possible to get the same response for the case that hash(Ci) != hash(Zj) but they both apply the same changes (say, for instance, they both add the same line in the same file but they differ in the commit message)?


Edit

By saying that a tag "contains a given commit's changes" I mean that in the tag's log history, there's a commit C applying the same modifications than the given commit.


Solution

  • The general I would think of would be :

    1. find the list of commits with the meaning "if a tag contains this commit, then it contains the changes"
    2. group together all the tags that contain any of these commits

    The hardest part is to determine 1., so first the easiest part :


    • for point 2. : If you have your list of commits :
    # in a file, or as the output of a commit :
    eacf32c
    44df701
    ...
    

    just run repeatedly git tag --contains, and merge the results together :

    cat thelist.txt | while read sha; do git tag --contains $sha; done | sort -u
    

    • for point 1 : find the list of commits

    this depends a lot on how you intend to target said changes. Here are some examples :

    • if you know you only want to target commits applied using git cherry-pick, chances are the commit messages will be a lot similar : git log --all --grep "one discriminating sentence" or git log --all --grep "#issuenumber" will probably give you a good starting point

    • if you want to target commits that added or removed one specific line : start from git log --all -S "a specific line", or git log --all -G "a specific line" (read the doc for -S and -G if you need more explanations on how they work).

    You can also spot a change in that/specific/file.txt or that/specific/dir by running :

    git log -S "a specific line" -- that/specific/file.txt
    git log -G "a specific line" -- that/specific/dir
    
    • if you want to spot commits that introduced the exact same patch to your code base, it turns out git has an algorithm for that : git patch-id
      (note : it is used in git rebase to spot commits that are already applied on target)

    Using it on the complete list of commits of your repo will be a bit involved, but it can be automated :

    first compute the patch-id for the changes you want to spot :

    $ git show A | git patch-id --stable
    # this will give you a two fields output :
    # a hash for the diff itself             the hash of the commit
    3bbe48e8ce5714b78900653e618ff4dc41205ffb 84d48dcd44d440d3a35177c92c897d2efae93346
    

    then compute all patch ids, and grep for the one you want :

    git rev-list --all | while read sha; do git show $sha | git patch-id; done
      | grep "^3bbe48e8ce5714b78900653e618ff4dc41205ffb"
    

    the second column will be the list of commits : ... | awk '{ print $2 }'

    • rather than listing all commits in your repo (git rev-list --all), you may want to narrow the initial search with one of the first commands : feed in the list produced by git log --grep ... or git log -S ... or git log -- that/specific/dir ...

    • you can use the same trick for "the patch on a specific file" by changing the git show commands :

    git show $sha -- file1 file2 dir3 | git patch-id
    
    • you can write your own script, that takes in a commit id, and checks if it contains what you want

    • etc, etc, etc ...

    Note : if you have a git log command that allows you to spot some commits you are interested in, you can turn it in a command which only outputs the commit hashes, by removing all fomatting options (--onelin, --graph, --format ...) and replace them with --format="%H"