0

I would like to be able to retrieve the latest revision hash of a subdirectory on a remote repo. I can get the revision hash of a subdirectory on the LOCAL repo using this command (CORRECTED):

git rev-parse HEAD:path/to/subdir

(see How to retrieve the hash for the current commit in Git?)

And I can get the revision hash of the HEAD of a remote repo using this command:

git ls-remote ssh://git@myserver.com/repo-name.git HEAD

(see Getting the last commit hash from a remote repo without cloning)

But I haven't found a way to combine the two, i.e., to get the subdirectory hash from the remote repo without retrieving the code. Is that possible?

EDIT FOR CLARIFICATION:

I'm looking for a command that can be run without having a local repo at all, just as we can execute git ls-remote without having a local copy of the repo.

ALSO: Edited first example for copy-paste errors. (Thanks to @torek for pointing out the error.)

For context, I would like to write a script that can check if changes have been made to specific subdirectories. The command git rev-parse give thes exact information that I'm looking for, but it must be run on the local repo.

Lee Jenkins
  • 2,299
  • 3
  • 24
  • 39

1 Answers1

2

I'm looking for a command that can be run without having a local repo at all, just as we can execute git ls-remote without having a local copy of the repo.

You'd have to run a command on the remote—i.e., have shell access there, or have someone on the inside there who gives you a backdoor way to run the command. For instance, if the remote is host2.example.com, you might do:

ssh host2.example.com 'git -C /path/to/repo rev-parse ...'

This, of course, requires that you be able to log in to that host. If you can't log in to that host, then no, you'll need to clone the repository first.


Meanwhile, back to the rev-parse command. In your case this seems to be sufficient, but just in general:

git rev-parse HEAD:path/to/subdir

is very different from the command shown in Latest commit hash of subdirectory (this was linked at some point, so now it is via this answer):

git log -n 1 --format="%h %aN %s %ad" -- $directory

What git rev-parse HEAD:path/to/subdir prints is the hash ID of the internal Git tree object that stores path/to/subdir within the current commit. But what git log -n 1 ... $directory prints is the hash ID of some commit. This is never the hash ID of any Git internal tree object.

The commit that this git log finds is the first commit that satisfies some constraint, within in the sequence of commits formed by enumerating commits the way git log usually does, one at a time starting from HEAD and working backwards. The constraint in question is that, by comparing (as with git diff) the (single) parent commit of some non-merge commit to the commit itself, in that pair of commits, some file(s) within path/to/subdir changed.

That is, suppose we have the following partial commit graph:

...--F--G--H   <-- master (HEAD)

where H is some commit hash ID, G is the parent of H, and F is the parent of G. (Note that there are no merge commits in this graph-fragment.)

Suppose further that if we compare the snapshot in G vs the snapshot in H, the only file changed is README.md. Hence git log -p would show that in H we changed README.md. However, when comparing the snapshot in F vs the snapshot in G, file path/to/subdir/foo.cc has changed. So git log -p would show that in G, we changed a file within path/to/subdir.

Since git log starts by looking at H (and thus compares G-vs-H), but then goes on to look at G (and thus compares F-vs-G), the command:

git log -n 1 --format="%h %aN %s %ad" -- $directory

will print the abbreviated hash (%h) of commit G, the author name (after applying .mailmap, %aN) from commit G, the subject line (%s) from commit G, and the author date (%ad) from commit G.

But running:

git rev-parse HEAD:path/to/subdir

prints the full hash ID of the tree object in commit H that holds information about any sub-trees and blobs that are required for the snapshot that is part of commit H.

If you want to enumerate (potentially many) commit hash IDs, with or without applying various constraints, you use git log or git rev-list to follow the commit graph. That's what the git log command does, after all: walk the commit graph, starting at the starting-points you request (or at HEAD if you don't request any particular starting-points) and showing commits.

If you want one particular commit hash ID and you know enough about that commit, you can use git rev-parse. In some cases, git rev-parse can also walk the commit graph: in particular it can find commits whose commit log message contains some string or regular expression. But it is not smart enough to examine the diffs from one commit's parent(s) to that commit; for that, you need git log or its plumbing sister command, git rev-list.

(Note also that by default, git log ignores the diffs of any merge commits. That's because the diff of a merge is complicated by the fact that there are two or more parent snapshots: Which parent should Git use when making a diff? To avoid answering that question, git log just doesn't bother to make a diff at all, by default.)

torek
  • 448,244
  • 59
  • 642
  • 775
  • So, TL;DR is "you can't" ? – Lee Jenkins Apr 01 '19 at 19:42
  • 1
    You can't, unless you have the ability to run Git commands on the remote. In which case, run the desired command on the remote. Lacking that, clone, then run the desired command on the local clone. – torek Apr 01 '19 at 20:18
  • I appreciate your answer. For the benefit of others that may find this answer later, it would be helpful if those three sentences (in your comment) were the first three sentences of the answer. – Lee Jenkins Apr 02 '19 at 12:53