9
  • Which information I have: the name of the file (myfile.txt) on the master branch. The file has been modified the last time with commit "3" resulting into version 2.

  • What I want to retrieve: the tag name "0.0.2" which includes the commits "3" and "4" since the last tag "0.0.1".

enter image description here

  • What I know:

(A) How do I get the changed files between 2 tags (see here):

git diff --name-only 0.0.1 0.0.2

This prints 'myfile.txt' among others.

(B) Normally that should work exactly for what I need (see here):

git describe --always `git log --pretty=format:%H -n 1 myfile.txt`

But then I don't get the tag name '0.0.2' or the commit related to this tag. Instead, I get the commit SHA-1 of commit 3, which is includes the newest changes of myfile.txt.

(C) A tag is annotated, if the following command prints the respective tag name:

git for-each-ref --format='%(refname) %(objecttype)' refs/tags

which prints:

refs/tags/0.0.1 tag
refs/tags/0.0.2 tag
refs/tags/0.0.3 tag
refs/tags/0.0.4 tag

Question

So my questions are: is the way (B) the right one for my purpose? If yes, how do I change it do get the desired tag name? Or is there another way than (B) to get what I need?

tangoal
  • 724
  • 1
  • 9
  • 28
  • What existing tags do you have? Are they *annotated* tags or *lightweight* tags? (Use `git for-each-ref --format='%(refname) %(objecttype)' refs/tags` to display them all.) – torek May 28 '18 at 23:25
  • @torek: I have some output like this: refs/tags/0.0.1 tag refs/tags/0.0.2 tag refs/tags/0.0.3 tag refs/tags/0.0.4 tag Does this mean they are annotated? I always tried to use annotated tags (option -a) when tagging, but for this project I'm not sure, if I did it consequently. – tangoal May 29 '18 at 06:05
  • Yes, the `tag` word means it's an annotated tag. This matters because that's the kind of tag that the default `git describe` requires. It sounds like your request should have worked, though given the diagram, the description would be `0.1.1-1-g`: `git describe` finds an *earlier* tag rather than a *later* one. With `--always` you should never get an error from `git describe`, so that's extra puzzling. – torek May 29 '18 at 06:17
  • @torek: I applied the '--always' option wrongly. It is an argument of 'git describe', but I used it as an argument of 'git log'. However, it does not seem to return what I expected: Instead of returning the tag name 0.0.2 (related to commit 4), it returns the SHA-1 of commit 3 (where 'myfile.txt' has been modified at last). – tangoal May 29 '18 at 07:59
  • I think `git log --source --all myfile.txt` is enough, it shows the tag name in log. – bimlas May 31 '18 at 08:03

1 Answers1

6

tl;dr

git describe --contains `git log --pretty=format:%H -n 1 myfile.txt` | sed 's/\(.*\)[~^].*/\1/'

Documentation

The relevant flag descriptions from the git-describe documentation:

--always
Show uniquely abbreviated commit object as fallback.

This isn't what you want. This is allowing a commit hash to be printed instead of tag.

--tags
Instead of using only the annotated tags, use any tag found in refs/tags
namespace. This option enables matching a lightweight (non-annotated) tag.

This is closer, as it allows utilization of all tags, annotated or not. However, it's still subject to the default behavior of finding the tag before the last commit.

--contains
Instead of finding the tag that predates the commit, find the tag that comes
after the commit, and thus contains it. Automatically implies --tags.

Bingo.

Example Usage

Given a repository with the following commit history:

$ git log --decorate=short -p | grep -v Author
commit d79ae00046a3ce456316fb431af5c4473a9868c8 (HEAD -> master, tag: v0.0.3)
Date:   Mon May 28 22:54:33 2018 -0700

    Commit #5

diff --git a/foo.txt b/foo.txt
index 257cc56..3bd1f0e 100644
--- a/foo.txt
+++ b/foo.txt
@@ -1 +1,2 @@
 foo
+bar

commit 7921bbcd4bb0712e4b819231829bed5a857f99a5
Date:   Mon May 28 22:54:11 2018 -0700

    Commit #4

diff --git a/test.txt b/test.txt
index 7698346..fadbf1d 100644
--- a/test.txt
+++ b/test.txt
@@ -1,3 +1,4 @@
 test1
 test2
 test3
+test4

commit fbe5a73bc2b5edcd3cb7afa26b80f8ecb12f982d (tag: v0.0.2)
Date:   Mon May 28 22:53:28 2018 -0700

    Commit #3

diff --git a/test.txt b/test.txt
index bae42c5..7698346 100644
--- a/test.txt
+++ b/test.txt
@@ -1,2 +1,3 @@
 test1
 test2
+test3

commit 794519596d9e2de93ec71686a1708e5f81fbba21
Date:   Mon May 28 22:52:51 2018 -0700

    Commit #2

diff --git a/test.txt b/test.txt
index a5bce3f..bae42c5 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1,2 @@
 test1
+test2

commit 10f854c9c09ac6c4de10311ffb5809f09a1edd1a (tag: v0.0.1)
Date:   Mon May 28 22:52:00 2018 -0700

    Commit #1

diff --git a/foo.txt b/foo.txt
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/foo.txt
@@ -0,0 +1 @@
+foo
diff --git a/test.txt b/test.txt
new file mode 100644
index 0000000..a5bce3f
--- /dev/null
+++ b/test.txt
@@ -0,0 +1 @@
+test1

Note that the file test.txt is edited in commits #1-4, but not #5. Commit #5 is tagged with v0.0.3, which is what we want as output.

Running just the git commands produces this output:

$ git describe --contains `git log --pretty=format:%H -n 1 test.txt`
v0.0.3~1

The ~1 indicates that the last change to the file is 1 commit behind the tag provided. Piping to sed gets the tag all by itself, for completeness sake.

$ git describe --contains `git log --pretty=format:%H -n 1 test.txt` | sed 's/\(.*\)[~^].*/\1/'
v0.0.3
chuckx
  • 6,484
  • 1
  • 22
  • 23
  • When I execute it on my project, then the desired tag name is returned, but also an additional "^2", which I can't find in the git describe documentation. `v0.0.2^2~1`: The ~1 seems clear to me. – tangoal May 31 '18 at 21:31
  • 1
    `^2` means "the second parent of a merge commit" (i.e. a commit has multiple parents). The `~1` means the parent commit. So, `^` traverses horizontally between parents at a given level, while ~ traverses vertically from parent to grandparent. So, `^2~1` means "the parent of the second parent". I'll update the sed regular expression in my answer to account for this. The syntax is described [here in the **Ancestry References** section](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection). – chuckx May 31 '18 at 22:08