1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

gitcommit.cmake: work around Windows pathname aliasing.

The cmake script that determines the current git head commit, in order
to bake it into the binaries for development builds from a git
checkout, wasn't working reliably on Windows: sometimes it reported
that the source directory didn't seem to be a git repository, when in
fact it was.

This occurred because gitcommit.cmake starts by trying to work out
_whether_ the source directory is the top level of a git worktree at
all, by the technique of running `git rev-parse --show-toplevel` (to
print the top-level path of the git worktree _containing_ $PWD, if
any) and comparing it with ${CMAKE_SOURCE_DIR}. But the comparison was
done as a plain string, which leads to problems if more than one
string can represent the same actual directory.

On Windows, this can occur for two reasons that I know of. One reason
is related to Windows itself: if you map a network file server path to
a local drive letter, then the same directory is accessible as a UNC
path (e.g. \\hostname\share\subdir) and via the drive letter (e.g.
N:\subdir). And it can happen that CMAKE_SOURCE_DIR and git's output
disagree on which representation to use, causing the string comparison
to return a false negative.

(This can also affect filesystems in a WSL instance, accessed from
native Windows via \\wsl$\instance\path, because Windows implements
that as a network file share even though the network in question is
purely in the imagination of that one machine.)

The other reason is related more specifically to git, because some
versions of Windows git are built on top of MSYS or MINGW or that kind
of shim layer, and model Windows drive letters as subdirectories of a
Unixlike VFS root. So you might also find that the two strings
disagree on whether you're in C:\Users\alice\src\putty or
/c/Users/alice/src/putty.

I think this commit should work around both of these problems. Reading
the man page for `git rev-parse` more carefully, it has an option
`--show-cdup`, which returns a _relative_ path from $PWD to the
top-level directory of the worktree: that is, it will be a sequence of
`../../..` as long as necessary, including length zero. So you can use
that to directly query whether you're at the top of a git worktree: if
`git rev-parse --show-cdup` returns the empty string and a success
status, then you are. (If you're deeper in a worktree it will return a
non-empty string, and if you're not in a worktree at all it will
return a failure status and print an error message to stderr.)
This commit is contained in:
Simon Tatham 2024-12-27 10:07:32 +00:00
parent 193e2ec063
commit d96a4983be

View File

@ -6,34 +6,32 @@ set(commit "${DEFAULT_COMMIT}")
set(TOPLEVEL_SOURCE_DIR ${CMAKE_SOURCE_DIR})
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --show-toplevel
OUTPUT_VARIABLE git_worktree
COMMAND ${GIT_EXECUTABLE} rev-parse --show-cdup
OUTPUT_VARIABLE path_to_top
ERROR_VARIABLE stderr
RESULT_VARIABLE status)
string(REGEX REPLACE "\n$" "" git_worktree "${git_worktree}")
string(REGEX REPLACE "\n$" "" path_to_top "${path_to_top}")
if(status EQUAL 0)
if(git_worktree STREQUAL CMAKE_SOURCE_DIR)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
OUTPUT_VARIABLE git_commit
ERROR_VARIABLE stderr
RESULT_VARIABLE status)
if(status EQUAL 0)
string(REGEX REPLACE "\n$" "" commit "${git_commit}")
else()
if(commit STREQUAL "unavailable")
message("Unable to determine git commit: 'git rev-parse HEAD' returned status ${status} and error output:\n${stderr}\n")
endif()
endif()
# If we're at the top of a git repository, --show-cdup will print the
# empty string (followed by a newline) and return success. If we're
# lower down, it will return a sequence of "../../" that leads to the
# top; if we're higher up it will fail with an error.
if(status EQUAL 0 AND path_to_top STREQUAL "")
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
OUTPUT_VARIABLE git_commit
ERROR_VARIABLE stderr
RESULT_VARIABLE status)
if(status EQUAL 0)
string(REGEX REPLACE "\n$" "" commit "${git_commit}")
else()
if(commit STREQUAL "unavailable")
message("Unable to determine git commit: top-level source dir ${CMAKE_SOURCE_DIR} is not the root of a repository")
message("Unable to determine git commit: 'git rev-parse HEAD' returned status ${status} and error output:\n${stderr}\n")
endif()
endif()
else()
if(commit STREQUAL "unavailable")
message("Unable to determine git commit: 'git rev-parse --show-toplevel' returned status ${status} and error output:\n${stderr}\n")
message("Unable to determine git commit: top-level source dir ${CMAKE_SOURCE_DIR} is not the root of a repository")
endif()
endif()