Does MacOS on M1 prioritise arm binaries later on path?

I'm trying to nix my new M1 Mac with home-manager and I'm thinking I might wanna skip xcode devtools altogether.

I've installed nix and home-manager and built a generation with git, but:

viktor@Viktors-MacBook-Air result % which git                              
/usr/bin/git

So this is the fake Mac binary that prompts to install devtools.

However, my real git from nix is before on PATH:

viktor@Viktors-MacBook-Air result % echo $PATH                              
/Users/viktor/.nix-profile/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

Only thing I can think of is that MacOS somehow continues traversing PATH until it finds a arm64 executable?

viktor@Viktors-MacBook-Air result % file /Users/viktor/.nix-profile/bin/git
/Users/viktor/.nix-profile/bin/git: Mach-O 64-bit executable x86_64

viktor@Viktors-MacBook-Air result % file /usr/bin/git                      
/usr/bin/git: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
/usr/bin/git (for architecture x86_64): Mach-O 64-bit executable x86_64
/usr/bin/git (for architecture arm64e): Mach-O 64-bit executable arm64e

I have rosetta installed and the real git works fine from full path.

I am using zsh as the shell

Am I misunderstanding the executable resolution? There's no alias at play at least, but is there something else going on?


Solution 1:

This was just zsh caching at play.

You need to run rehash after you change $PATH which rebuilds zsh cache and then executables are found as expected.

See Section 3.1 of the Z shell guide http://zsh.sourceforge.net/Guide/zshguide03.html

There is a subtlety here. The shell tries to remember where the commands are, so it can find them again the next time. It keeps them in a so-called hash table', and you find the word hash' all over the place in the documentation: all it means is a fast way of finding some value, given a particular key. In this case, given the name of a command, the shell can find the path to it quickly. You can see this table, in the form key=value', by typing hash'.

In fact the shell only does this when the option HASH_CMDS is set, as it is by default. As you might expect, it stops searching when it finds the directory with the command it's looking for. There is an extra optimisation in the option HASH_ALL, also set by default: when the shell scans a directory to find a command, it will add all the other commands in that directory to the hash table. This is sensible because on most UNIX-like operating systems reading a whole lot of files in the same directory is quite fast.

The way commands are stored has other consequences. In particular, zsh won't look for a new command if it already knows where to find one. If I put a new ls command in /usr/local/bin in the above example, zsh would continue to use /bin/ls (assuming it had already been found). To fix this, there is the command rehash, which actually empties the command hash table, so that finding commands starts again from scratch. Users of csh may remember having to type rehash quite a lot with new commands: it's not so bad in zsh, because if no command was already hashed, or the existing one disappeared, zsh will automatically scan the path again; furthermore, zsh performs a rehash of its own accord if $path is altered. So adding a new duplicate command somewhere towards the head of $path is the main reason for needing rehash.