Gabriel
Gabriel

Reputation: 9442

Does git create new files during merge or overwrite existing ones

I have a script test.sh

#!/bin/bash
echo start old file
sleep 20
echo end old file

in the repository which I do execute, and in the mean time I git merge other-branch changes like

#!/bin/bash
echo start new file
sleep 20
echo end new file

into the current branch.

It seems that git on Unix (?) does not directly overwrite the existing file node (?) and does instead rm test.sh and creates the new file. In that way its guaranteed that the script execution will always read the initial file test.sh and terminate with echo end old file.

Note: On my system (Ubuntu 20.04), while executing the script and directy overtwriting the content in an editor, results in executing the new code, which is bad...

Is that correct and is it also correct on Windows with git-for-windows?

Upvotes: 1

Views: 224

Answers (2)

Gabriel
Gabriel

Reputation: 9442

On Windows with git-for-windows I see the same behavior:

$ mklink /H test.sh.bak
$ fsutil hardlink list test.sh.bak
test.sh.bak
test.sh
$ git merge test
$ fsutil hardlink list test.sh.bak
test.sh.bak

Meaning the hard link did not get preserved, meanin a new file has been created.

Upvotes: 0

alani
alani

Reputation: 13079

I can't answer regarding Windows, but on Ubuntu 18.04 I can confirm that a git checkout or git merge will delete and recreate a changed file, rather than editing it in place. This can be seen in strace output, for example:

unlink("test.sh")                       = 0

followed later by

openat(AT_FDCWD, "test.sh", O_WRONLY|O_CREAT|O_EXCL, 0666) = 4

It can also be seen if you create a hard link to the file before the git command and then look again afterwards, you will see that you have two different inodes, with different contents. This is to be expected following deletion and recreation, whereas an in-place edit would have preserved the hard linking.

$ ls -l test.sh
-rw-r--r-- 1 myuser mygroup 59 Jun  5 17:04 test.sh

$ ln test.sh test.sh.bak

$ ls -li test.sh*
262203 -rw-r--r-- 2 myuser mygroup 59 Jun  5 17:04 test.sh
262203 -rw-r--r-- 2 myuser mygroup 59 Jun  5 17:04 test.sh.bak

$ git merge mybranch
Updating 009b964..d57f33a
Fast-forward
 test.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

$ ls -li test.sh*
262219 -rw-r--r-- 1 myuser mygroup 70 Jun  5 17:05 test.sh
262203 -rw-r--r-- 1 myuser mygroup 59 Jun  5 17:04 test.sh.bak

You mentioned in a comment attached to the question that it is related to Overwrite executing bash script files. Although it would seem not to be the best idea to run a git command affecting a script which is currently still being executed, in fact the delete and recreate behaviour should mean that the existing execution will be unaffected. Even if the bash interpreter has not yet read the whole file into memory, it will have an open filehandle on the existing inode and can continue to access its contents even though that inode is no longer accessible via the filename that it had. See for example What happens to an open file handle on Linux if the pointed file gets moved or deleted

Upvotes: 4

Related Questions