1

After merging I got a symbolic links conflict:

$ git status
[SKIP]
both added:      file.txt
[SKIP]

Git diff doesn't show the target values:

$ git diff
[SKIP]
diff --cc file.txt
index 5873c9d,e31df9a..0000000
--- a/file.txt
+++ b/file.txt

Is there a simple way to compare symbolic link target values?

Dmitry Petrov
  • 1,490
  • 1
  • 19
  • 34
  • That's curious; Git *should* show the symlink contents. It does normally, e.g., comparing a symlink to the empty string when a new symlink is created. Perhaps the special combined diff from an in-progress merge is missing some code. – torek Oct 05 '17 at 00:02
  • Are they really symlink? Or just regular files, with a different file mode? (https://stackoverflow.com/q/6835796/6309) – VonC Oct 05 '17 at 04:57
  • @VonC yes, real symlinks. If you resolve one it will point to an actual target file. And the target files are different - let's imagine `file.txt` from branch1 points to `text/file_v3.txt` and `file.txt` from master points to `text/file_v2.txt`. I'd like to have a simple command that will show the symlinks diff: ```-text/file_v2.txt +text/file_v3.txt``` – Dmitry Petrov Oct 05 '17 at 17:36

1 Answers1

1

Using git show, you could display the target for that link.
I just tested it on a test repo, with a link referencing the file one or the file two in different branches.
The merge has a conflict:

vonc@voncavn7:/mnt/d/git/tests/sml/a$ git commit -m "add link from one"
[one 22b4906] add link from one
 1 file changed, 1 insertion(+)
 create mode 120000 link
vonc@voncavn7:/mnt/d/git/tests/sml/a$ git merge -s recursive two
Auto-merging link
CONFLICT (add/add): Merge conflict in link
Automatic merge failed; fix conflicts and then commit the result.
vonc@voncavn7:/mnt/d/git/tests/sml/a$ git diff
diff --cc link
index 43dd47e,1ed3c7a..0000000
--- a/link
+++ b/link

But I can see which one points to which:

vonc@voncavn7:/mnt/d/git/tests/sml/a$ git show :2:link
one
vonc@voncavn7:/mnt/d/git/tests/sml/a$ git show :3:link
zero

Note that with Git 2.16.x/2.17, you would be able to resolve it automatically, should you chose a merge option.
That is because "git merge -Xours/-Xtheirs" learned to use our/their version when resolving a conflicting updates to a symbolic link.

See commit fd48b46 (26 Sep 2017) by Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster -- in commit 14b9d9a, 23 Jan 2018)

The -Xours/-Xtheirs merge options were originally defined as a way to "force" the resolution of 3way textual merge conflicts to take one side without using your editor, hence did not even trigger in situations where you would normally not get the <<< === >>> conflict markers.

This was improved for binary files back in 2012 with a944af1 ("merge: teach -Xours/-Xtheirs to binary ll-merge driver", 2012-09-08, Git v1.7.12.4).

Teach a similar trick to the codepath that deals with merging two conflicting changes to symbolic links.


Note that with Git 2.24 (Q4 2019), a bug in merge-recursive code that triggers when a branch with a symbolic link is merged with a branch that replaces it with a directory has been fixed.

See commit 83e3ad3 (18 Sep 2019) by Jonathan Tan (jhowtan).
Helped-by: Elijah Newren (newren).
(Merged by Junio C Hamano -- gitster -- in commit 1f4485b, 07 Oct 2019)

When the working tree has:

- bar (directory)
- bar/file (file)
- foo (symlink to .)

(note that lstat() for "foo/bar" would tell us that it is a directory)

and the user merges a commit that deletes the foo symlink and instead contains:

- bar (directory, as above)
- bar/file (file, as above)
- foo (directory)
- foo/bar (file)

the merge should happen without requiring user intervention.
However, this does not happen.

This is because dir_in_way(), when checking the working tree, thinks that "foo/bar" is a directory.
But a symlink should be treated much the same as a file: since dir_in_way() is only checking to see if there is a directory in the way, we don't want symlinks in leading paths to sometimes cause dir_in_way() to return true.

Teach dir_in_way() to also check for symlinks in leading paths before reporting whether a directory is in the way.


With Git 2.33 (Q3 2021), the "union" conflict resolution variant misbehaved when used with binary merge driver.

See commit 382b601, commit 7f53f78, commit 7d879ad (10 Jun 2021) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit 308528a, 13 Jul 2021)

ll_binary_merge(): handle XDL_MERGE_FAVOR_UNION

Signed-off-by: Jeff King

Prior to commit a944af1 ("merge: teach -Xours/-Xtheirs to binary ll-merge driver", 2012-09-08, Git v1.8.0-rc0 -- merge listed in batch #7), we always reported a conflict from ll_binary_merge() by returning "1" (in the xdl_merge and ll_merge code, this value is the number of conflict hunks).
After that commit, we report zero conflicts if the "variant" flag is set, under the assumption that it is one of XDL_MERGE_FAVOR_OURS or XDL_MERGE_FAVOR_THEIRS.

But this gets confused by XDL_MERGE_FAVOR_UNION.
We do not know how to do a binary union merge, but erroneously report no conflicts anyway (and just blindly use the "ours" content as the result).

Let's tighten our check to just the cases that a944af1 meant to cover.
This fixes the union case (which existed already back when that commit was made), as well as future-proofing us against any other variants that get added later.

Note that you can't trigger this from "git merge-file --union"(man), as that bails on binary files before even calling into the ll-merge machinery.

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