0

When I do a git merge, is it standard behavior that git sets a new timestamp on all merged files? So the original timestamp of a file only exists on the original branch where the file was modified/created?

Benni
  • 1,023
  • 11
  • 15
  • How is your diff meaningful? Did you compare the files at the merge commit against _both_ parents? – Tim Biegeleisen Oct 06 '17 at 08:00
  • @Tim Biegeleisen: You are right, I just diff-compared with the 'develop' branch history. When I compare against the 'master' history, there is a `^M` character at the end of each line - so the modification is because of altered line endings – Benni Oct 06 '17 at 08:15
  • 1
    git doesn't maintain file metadata including timestamps; only executable bit is stored and restored. See https://stackoverflow.com/q/2179722/7976758 – phd Oct 06 '17 at 11:24

1 Answers1

0

As phd mentioned in a comment, Git (mostly) does not use or care about timestamps on files.

Git does keep a timestamp per commit. More precisely, it keeps two: an author timestamp and a committer timestamp. But this has no effect on the file timestamps at all. Instead, Git leaves those up to the underlying operating system. If Git must update a file, it simply does so—writes to the file—and lets the OS change the file's modifcation-timestamp to "now" as a side effect. If Git must read a file, it simply does so, and lets the OS change the file's access-timestamp to "now" as a side effect. The timestamp most people mostly care about is the modification time.1

Hence, git merge touches a file in the work-tree, updating its time-stamp to "now", if and only if it needs to write on the file as a result of the merge. So the question really boils down to "why did Git need to write on the file in order to merge it?" The answer to that appears in your comments: the modification is because of altered line endings.

Merge works by finding a common merge base commit. This merge base commit is where the history from your current (HEAD) commit joins up with the history from the commit you are merging. Git then runs two git diffs, one from the merge base to your commit—this finds out "what you changed" for --ours—and one from the merge base to the other commit, to find out what they changed for --theirs. The merge action, the verb form of "to merge", then consists of combining these two sets of changes.2

If one set of changes is "change line endings", and this touches at least one line in every file, it makes sense that every file would be altered by the merge process. But note that Git can modify line endings on its own, through core.autocrlf and/or .gitattributes settings. The git merge code has a special case inside it, controlled by the merge.renormalize configuration setting, or by the -X renormalize or -X no-renormalize command-line options. Full use of this is beyond the scope of this answer, but the point of the renormalize flag is specifically to allow this kind of merge to work without having to touch every line.


1There are up to four total time-stamps per file, depending on the OS and file system: the "access time", called st_atime in a struct stat; the "modification time", st_mtime; a "change" time, st_ctime, and a "birth" time—the time the file was created—called st_birthtime or st_birthtimespec. The modification time and change time are similar, but not the same: for instance, changing the mode of a file, to add or remove the execute bit, changes the st_ctime but not the st_mtime. This mode-change is a change to information about the file that does not change the contents of the file. Most OSes also let users set the access and modification times arbitrarily, using a utime or utimes family of system calls; updating these times sets the ctime to "now".

2Eventually, after the combining, Git normally goes on to make a merge commit—the adjective or even noun form of "merge", since "a merge commit" is often abbreviated to just "a merge". A merge, or a merge commit, is a commit with a forked history: it points back not just to the earlier HEAD commit, but also to their commit, the one that was the source of --theirs, during the merge. This merge commit serves to join up the two histories, and hence affects every future merge, since the two histories now join up at that point, rather than at some earlier point. In other words, this merge-as-a-noun creates a new merge base for future merges.

torek
  • 448,244
  • 59
  • 642
  • 775