0

From the command prompt in windows i want to have the list of every hash (SHA1) a specific file already got after every commit made on it.

With i can find the actual hascode with "git has-object ", but i want to have the previous ones too.

  • The OP has indicated the `cmd` tag. That is for Microsoft Windows `cmd.exe`. – lit Aug 29 '21 at 19:21
  • @lit then OP is going to have to learn to translate from shell to cmd at some point, might as well be early. cmd.exe is so bad even Microsoft is on record calling its quoting and escaping behavior a botch, and those aren't its only problems. That doesn't make it unusable, it's not really deserving of anything comparable to Dijkstra's remark about BASIC, but it's close enough to that bad to bring the remark to mind. – jthill Aug 30 '21 at 23:09

2 Answers2

4

Fastest:

git rev-list --objects @ -- path/to/it | grep /it$

The rev-list lists the selected commits (the ones in the current history that touched path/to/it) and since you asked nice also the relevant objects: the tree ids for path and path/to in those commits and also the new blob id for path/to/it. You just want the blob id's.

Perhaps neater, and more flexible:

git log --pretty='%h:path/to/it %h path/to/it' -- path/to/it \
| git cat-file --batch-check='%(objectname) %(rest)'
jthill
  • 55,082
  • 5
  • 77
  • 137
  • Handy trick, and, if the proposal to allow `git rev-list` to become more flexible ever gets anywhere, we might even be able to get rid of the `| grep` part. – torek Aug 30 '21 at 08:31
  • @torek can't get rid of it without replacing it with something, like `--grep` instead of `| grep`. Is that such an improvement it'd justify importing all of `grep`'s capabilities (or leaving them out and running straight into Greenspun's Tenth)? – jthill Aug 30 '21 at 12:10
  • If `git rev-list` had a `--format` style option, `--format=%O` (to suppress the hash ID of the commit and print just the `--objects` part?) would do the job ... the default `--format` would be `%H`, more or less. I'm not sure exactly what the state of the proposed rev-list change is at this point. I guess you're noting that the objects include the `path` and `path/to` trees as well though. Not sure what if anything the proposed changes have to say about such things. – torek Aug 30 '21 at 13:45
1

Each commit you make contains every file. (Well, every file it contains. That is: if you made commit deadbee six weeks ago and there were six files back then, commit deadbee has six files in it. If newer commit cabfeed has nine files, it has 9 files in it. If you then removed one of them and just now made commit fabdeed, that commit has eight files, because you removed one.)

Given some commit hash ID, and the path of some file within that commit hash ID, you can find the corresponding "blob hash ID" of that file in that commit. For instance, one of the most recent commits in the Git repository for Git itself is c4203212e360b25a1c69467b5a8437d45a373cac. This commit contains 3971 files (well, one of them is a submodule, so 3970 files is more accurate). One of these files is named builtin/tag.c. Let's see its hash ID, using the git rev-parse command:

$ git rev-parse c4203212e360b25a1c69467b5a8437d45a373cac:builtin/tag.c
452558ec95751965c5eb83b96220e411c3bc29d2

This means 452558ec95751965c5eb83b96220e411c3bc29d2 contains the data for that file, as we can see with a bit more effort:

$ git cat-file -p 452558ec95751965c5eb83b96220e411c3bc29d2 | head -2
/*
 * Builtin "git tag"

So, what you need Git to do here is iterate through each commit, just like git log does. Now, git log has no option to print the hash IDs of each file (or any specific file) within the commit, but git log and its sister command git rev-list will, when you use them the right way, print out the hash ID of each commit. So that gets you just enough to write a program to do what you want done.

This program is very easy to write in shell-script commands, which can be run with /bin/sh or bash. Git needs some shell so on systems that don't have one, the Git installation usually includes bash. Here's a minimal bash program that runs git rev-parse over and over again in the right way:

git rev-list HEAD | while read hash; do git rev-parse $hash:builtin/tag.c; done

Running this briefly produces:

452558ec95751965c5eb83b96220e411c3bc29d2
452558ec95751965c5eb83b96220e411c3bc29d2
452558ec95751965c5eb83b96220e411c3bc29d2
452558ec95751965c5eb83b96220e411c3bc29d2
452558ec95751965c5eb83b96220e411c3bc29d2
452558ec95751965c5eb83b96220e411c3bc29d2
452558ec95751965c5eb83b96220e411c3bc29d2
452558ec95751965c5eb83b96220e411c3bc29d2
452558ec95751965c5eb83b96220e411c3bc29d2
82fcfc0982423f3c7ee90fe43d30295023cc7873
82fcfc0982423f3c7ee90fe43d30295023cc7873
82fcfc0982423f3c7ee90fe43d30295023cc7873
[I interrupted at this point]

which, as you can see, shows the hash ID for each copy of tag.c in each commit.

You'll no doubt want to improve this program to print each hash ID only once, or perhaps once per commit-that-has-a-different builtin/tag.c in it, than the later commit printed earlier (remember that Git works backwards, which is why git log starts with the latest commit and works backwards). You will probably also want to make sure that each commit of interest actually has the file: if you go far enough back in the Git repository for Git, there ceases to be a builtin/tag.c at all. Before that point all the builtins were in the top level, spelled builtin-whatever, so it was builtin-tag.c instead.

Unless you use PowerShell, the Windows command prompt is generally not a usable programming language, so you'll want to write your program in a more capable language. Remember the following:

  • git log can be told to print just the commit hash ID;
  • git log can be told to --follow file renames (rev-list cannot); and
  • git log can be told to print commit hash IDs only for commits where some file changed between this commit and its parent.

So git log may end up being more useful than git rev-list, depending on your needs here.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thank you for your well explicit details about how i can solve my issue. But i have a problem with this because it makes me write an extra shell script. I thought there where a much simpler way to get this done. Because i actually want to implement those git commands in my kotlin application and it will not be proper to add an extra shell script. I want to call a file from a git local repository and run a command that will list me every sha1 that the file already got. – uncletonton Aug 29 '21 at 19:45
  • See jthill's answer for a one line program (rev-list + grep), but one way or another you're stuck with writing a program. There's a proposal to fix up `git rev-list` that would let you just run `git rev-list` itself, but as of now, you're stuck with at least one intervening program. – torek Aug 30 '21 at 08:30