Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git: get the commit-id and the note of a commit

Tags:

git

I am writing a hook for validating url's which are entered in the notes section of the git log. I loop through each commit to get the note for that particular commit and do a string comparison for the url. The problem pops up if the commit is a new commit, since the new commit does not contain a note.

example:-

git add sample
git commit -m "added sample"
git notes add -m "sample note" <commitID-of-sample>
git push origin master
git push origin refs/notes/*

problem with the above example is, first i push the commit but the commit has no note since git push origin refs/notes/* is pushed after master. I basically want to access the note of a commit in the pre-receive hook.

any suggestions?

like image 886
Iowa Avatar asked Dec 11 '22 12:12

Iowa


1 Answers

You literally can't do this the way you showed. The problem boils down to the way notes work. As mart1n suggested in a comment, you need to push the notes either first, or simultaneously. Here's why:

How notes work

A note "attached to" a commit exists (and thus can be shown by git log and git show) "now" if:

  1. Let's say the SHA1 of this commit (whose note we're trying to find) is 1234567....
  2. refs/notes/commits exists, and
  3. reading via the commit that refs/notes/commits points-to, there's a "file" whose file-name matches 1234567....

The note is the content of the file with the funny name.

Let's explore the process of finding a note.

Background, using low-level "raw" git commands

The lowest-level access command for most of the repo is git cat-file. This lets you look at the type (git cat-file -t sha1) and contents (git cat-file -p sha1) of any object within the repository. The sha1 part here can be any git reference name, as long as it resolves to one of those 40-character hexadecimal SHA-1 values. The default ref-name for notes is refs/notes/commits, and (if it exists) it should always point to a commit object. Hence:

$ git cat-file -p refs/notes/commits
tree 28db2757c2b7c6e4bbfef35e61e8bd7c698626dc
parent ce97e80bfbdab9bc163ecb93779d071d7ed8c739
author A U Thor <[email protected]> 1376652910 -0600
committer A U Thor <[email protected]> 1376652910 -0600

Notes added by 'git notes edit'

Our next step would be to look at the tree named here:

$ git cat-file -p 28db2757c2b7c6e4bbfef35e61e8bd7c698626dc

For a short set of notes this produces almost the same thing we'll see below. If there are a lot of notes, this leads to more trees, which in turn can have yet more subtrees, so it's a pain. There's a much easier way.

Obtaining the notes

To see what the current notes are (if any), use git notes list:

$ git notes list
b9458a531c3f93bd36af0025d56029ef58cf8d00 5e013711f5d6eb3f643ef562d49a131852aa4aa1
1c716d4d58325651ceecba14ce8974b0ac6d13e9 a546ad9299465c9cf304fecf01d1514337419e2f

The "note contents" use the SHA-1 on the left of each line, and each note-content-file is attached to the commit1 whose SHA-1 is on the right. Let's use the first line to see how this works:

$ git cat-file -t 5e013711f5d6eb3f643ef562d49a131852aa4aa1
commit
$ git cat-file -p 5e013711f5d6eb3f643ef562d49a131852aa4aa1
tree ead5cc295ae64c9186f392b80ca4ed10888f20d9
parent 309b36c8166f5ff330a6e8a0a674ed161d45b5f4
author ...[line snipped]
committer ...[line snipped]

add ast ... [rest snipped]

You can, of course, git show 5e0137 or git log -1 5e0137, etc., to see that commit, which will also show you the note contents. To see just the raw note contents, though, use the SHA-1 on the left:

$ git cat-file -p b9458a531c3f93bd36af0025d56029ef58cf8d00
experiment: add a note

this is the text I put in the note

Let's do a git notes edit 5e0137 and change the note for the commit, and then git notes list again:

$ git notes edit 5e0137
[editor session snipped]
$ git notes list
d87650a968ff684e69175eacde0880595f9f2989 5e013711f5d6eb3f643ef562d49a131852aa4aa1
1c716d4d58325651ceecba14ce8974b0ac6d13e9 a546ad9299465c9cf304fecf01d1514337419e2f

The two "file names" on the right are still exactly the same, but on the left, the first SHA1 is different. Let's look at it:

$ git cat-file -p d87650a968ff684e69175eacde0880595f9f2989
change some stuff in the note

I edited the note attached to 5e0137.

If you now git show (git log, etc) the commit the old note was attached to, you get the new note. What these do, in effect, is to run git notes list, check on the right for a matching ID, and if found, git cat-file -p the ID on the left (reformatted / indented). (Well, a more optimized version of that, of course.)

This mechanism, of looking up the commit ID in the notes list, is why notes can change.

Adding a new commit under refs/notes/commits adds/changes files under that "branch" (it's not really a branch, branches start with refs/heads/, but it's otherwise just like a branch). The new files have new notes that apply to existing (old) commits. Those old commits are not changed at all, but when git log and git show look at the notes to see if the commit-ID is in there, they get new, different notes to show for the same old commit.

The commit object named by refs/notes/commits must exist by now, though, and must point to the notes you want to see attached to the commit(s) you're asking about. If you push a bunch of new commits to some remote-repo, and have not pushed the refs/notes/commits commit yet, anything looking on the remote-repo can't see your notes, as they simply are not there. Push the notes first, or at the same time, and they will be there to find.


1Actually a note can list any SHA-1: a commit object, a blob object, an annotated tag object, or even a tree object. You could even put in SHA-1 values that do not correspond to any object in the repo, a "detached note" if you will. I know of no uses for such a thing, but there's no technical reason it could not be done. [Edit to add: git notes won't do this; I mean you could do it with git plumbing commands. I haven't tried it though.]

An oddity

Naturally, refs/notes/commits is itself a regular commit tree. We can therefore "go back in time" and look at what the notes looked like before the most recent git notes edit. But there seems to be no "front end" interface for this. I tried, e.g.:

$ git log -1 --notes=refs/notes/commits^ 5e0137

but that just shows no note at all, which seems weird / broken, since --notes=refs/notes/commits works.

like image 104
torek Avatar answered Dec 29 '22 18:12

torek