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

前端 未结 2 1309
时光说笑
时光说笑 2021-01-01 05:48

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

2条回答
  •  有刺的猬
    2021-01-01 06:18

    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  1376652910 -0600
    committer A U Thor  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.

提交回复
热议问题