2

I am using Github Desktop on macos. In my master and branch A I have the following files (extract):

├── src
│   ├── mct-tools
│   │   ├── resources
│   │   │   ├── settings
│   │   │   ├── version.json
│   │   └── windrose.rb
│   └── mct-tools.rb
└── version.json

I want to change the file version.json under resources to a symlink to version.json in the root directory.

The file now becomes

src/mct-tools/resources/version.json@ -> ../../../version.json

The problem is that when I do that and then try to merge 'master' into 'A', the change is not captured by Git and the file 'version.json' does not change.

If I instead remove the file in branch A the file is not created.

Any suggestion on how to track symlinks?

Rojj
  • 1,170
  • 1
  • 12
  • 32
  • is ```git add``` not working? – Kristian Jan 23 '21 at 18:54
  • @Kristian I am not using git directly, but Github desktop – Rojj Jan 23 '21 at 18:56
  • try ```git add .``` in terminal and commit. Git can track softlink file – Kristian Jan 23 '21 at 18:58
  • Tried `git add ./version.json ` from `resources` and then `git commit`. No difference – Rojj Jan 23 '21 at 19:53
  • 1
    shouldn't you do ```git add ./src/mct-tools/resources/version.json``` instead? – Kristian Jan 24 '21 at 07:15
  • Same result. When I commit after adding the file I get `nothing to commit, working tree clean`. – Rojj Jan 24 '21 at 08:05
  • 1
    Git should have no issues with symlinks on macOS: it should all Just Work. It's not at all clear what has gone wrong here. If you examine the commits that are to be merged, and the merge base commit as well—by diff-ing the merge base against the two branch tip commits—you should see that in one diff, the one from merge base to the tip of `branch-A`, the file's status (from `git diff --name-status`) should be `T`, type-change. Whether that represents a merge conflict at merge time depends on whether there's any change from base to `master`-tip. – torek Jan 24 '21 at 10:35
  • To find the merge base commit hash ID, run `git merge-base --all master branch-A`. If this produces one commit hash ID, that is the merge base. If it produces two or more hash IDs, you have a complicated situation, but this is pretty rare. – torek Jan 24 '21 at 10:36
  • Fixed. I deleted the link in master, committed changes and switched to branch 'A'. Interestingly the change was identified, but the file still was not there! I then merged master into A, switched to master, recreated the link, committed and all worked as expected. Note: I use Github Desktop and not git directly. – Rojj Jan 25 '21 at 07:33

1 Answers1

1

Check if the issue persists with Git 2.33+ (Q3 2021), which uses git merge ORT (now the default: "Ostensibly Recursive's Twin").

Also merging a branch with directory renames into a branch that changes the directory to a symlink was mishandled by the ort merge strategy, which has been corrected with Git 2.39 (Q4 2022).

See commit 2b86c10 (22 Oct 2022) by Elijah Newren (newren).
(Merged by Taylor Blau -- ttaylorr -- in commit 969230b, 30 Oct 2022)

merge-ort: fix bug with dir rename vs change dir to symlink

Reported-by: Stefano Rivera
Signed-off-by: Elijah Newren

When changing a directory to a symlink on one side of history, and renaming the parent of that directory to a different directory name on the other side, e.g. with this kind of setup:

Base commit: Has a file named dir/subdir/file
Side1:       Rename dir/ -> renamed-dir/
Side2:       delete dir/subdir/file, add dir/subdir as symlink

Then merge-ort was running into an assertion failure:

git: merge-ort.c:2622: apply_directory_rename_modifications: Assertion `ci->dirmask == 0' failed

merge-recursive did not have as obvious an issue handling this case, likely because we never fixed it to handle the case from commit 902c521 ("t6423: more involved directory rename test", 2020-10-15, Git v2.30.0-rc0 -- merge listed in batch #4) where we need to be careful about nested renames when a directory rename occurs (dir/ -> renamed-dir/ implies dir/subdir/ -> renamed-dir/subdir/).
However, merge-recursive does have multiple problems with this testcase:

  • Incorrect stages for the file: merge-recursive omits the stage in the index corresponding to the base stage, making git status(man) report "added by us" for renamed-dir/subdir/file instead of the expected "deleted by them".
  • Poor directory/file conflict handling: For the renamed-dir/subdir symlink, instead of reporting a file/directory conflict as expected, it reports "Error: Refusing to lose untracked file at renamed-dir/subdir".
    This is a lie because there is no untracked file at that location.
    It then does the normal suboptimal merge-recursive thing of having the symlink be tracked in the index at a location where it can't be written due to D/F conflicts (namely, renamed-dir/subdir), but writes it to the working tree at a different location as a new untracked file (namely, renamed-dir/subdir~B^0)

Technically, these problems don't prevent the user from resolving the merge if they can figure out to ignore the confusion, but because both pieces of output are quite confusing I don't want to modify the test to claim the recursive also passes it even if it doesn't have the bug that ort did.

So, fix the bug in ort by splitting the conflict_info for "dir/subdir" into two, one for the directory part, one for the file (i.e.
symlink) part, since the symlink is being renamed by directory rename detection.
The directory part is needed for proper nesting, since there are still conflict_info fields for files underneath it (though those are marked as is_null, they are still present until the entries are processed, and the entry processing wants every non-toplevel entry to have a parent directory).

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250