Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for symlinks on Windows #2

Closed
martinvonz opened this issue Jan 8, 2021 · 4 comments · Fixed by #2939
Closed

Add support for symlinks on Windows #2

martinvonz opened this issue Jan 8, 2021 · 4 comments · Fixed by #2939

Comments

@martinvonz
Copy link
Owner

martinvonz commented Jan 8, 2021

We currently don't support symlinks on Windows (in fact, the project doesn't even build on Windows because of that). The advice I got from augie@google.com and former hg contributor "bmp" was (please correct me if I'm wrong):

  • Prior to Windows 10: No symlink support
  • Windows 10 without dev mode: No symlink support
  • Windows 10 dev mode: use FFI to call CreateSymbolicLink
  • Don't use the symlink creates, because it creates "junctions", which is not what we want.

Rust's standard library has std::os::windows::fs::symlink_file and std::os::windows::fs::symlink_dir, which both seem to call CreateSymbolicLinkW, only with different flags. Perhaps we can always use the symlink_file version? It's still unclear to me what the effect would be if a "file symbolic link" points to a target that's actually a directory.

@bpollack
Copy link

bpollack commented Jan 8, 2021

Correct enough for government purposes.

To clarify, not to be annoying, but just in case it changes anything: Windows supports symlinks when Developer Mode isn't enabled, and has done so going all the way back to at least Vista. What actually changed was that, prior to Windows 10, you needed to be an admin to create them. The only (albeit major) thing Windows 10 with Developer Mode changes is that normal users can now create symlinks, rather than either needing to run a process as an admin or mucking about with group policy options (GPOs).

For reasons I'm not clear on, the underlying API for this (CreateSymbolicLinkW) still requires you to say whether you're linking a file or a directory. I have absolutely no idea why, but I don't //think// (from two minutes of mucking about) you can cheat, just pass 0x0, and be happy.

@martinvonz
Copy link
Owner Author

I think 0x0 is what std::os::windows::fs::symlink_file passes, so I guess we'll try to do that whether the target is a file or a directory. I don't have access to a Windows machine, but it sounded like Augie might be able to attempt it later. Thanks!

@quark-zju
Copy link
Contributor

quark-zju commented Feb 1, 2021

Based on tests using mklink /D vs mklink, I think symlink_dir is the one to be used. It seems to handle both the directory and file cases, while directory symlink crated by symlink_file won't allow cd into it.

@martinvonz
Copy link
Owner Author

Thanks for checking!

ilyagr added a commit that referenced this issue May 6, 2024
For example, 

```
<<<<<<< Conflict 1 of 3
+++++++ Contents of side #1
left 3.1
left 3.2
left 3.3
%%%%%%% Changes from base to side #2
-line 3
+right 3.1
>>>>>>>
```

or

```
<<<<<<< Conflict 1 of 1
%%%%%%% Changes from base to side #1
-line 3
+right 3.1
+++++++ Contents of side #2
left 3.1
left 3.2
left 3.3
>>>>>>>
```

Currently, there is no way to disable these, this is TODO for a future
PR. Other TODOs for future PRs: make these labels configurable. After
that, we could support a `diff3/git`-like conflict format as well, in
principle.

Counting conflicts helps with knowing whether you fixed all the
conflicts while you are in the editor.

While labeling "side #1", etc, does not tell you the commit id or
description as requested in #1176, I still think it's an improvement.
Most importantly, I hope this will make `jj`'s conflict format less
scary-looking for new users.

I've used this for a bit, and I like it. Without the labels, I would see
that the two conflicts have a different order of conflict markers, but I
wouldn't be able to remember what that means. For longer diffs, it can
be tricky for me to quickly tell that it's a diff as opposed to one of
the sides. This also creates some hope of being able to navigate a
conflict with more than 2 sides.

Another not-so-secret goal for this is explained in
#3109 (comment). The
idea is a little weird, but I *think* it could be helpful, and I'd like
to experiment with it.
scott2000 added a commit to scott2000/jj that referenced this issue Nov 17, 2024
Adds a new "ui.conflict-marker-style" config option. The "diff" option
is the default jj-style conflict markers with a snapshot and a series of
diffs to apply to the snapshot. New conflict marker style options will
be added in later commits.

The majority of the changes in this commit are from passing the config
option down to the code that materializes the conflicts.

Example of "diff" conflict markers:

```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side martinvonz#1
fn example(word: String) {
    println!("word is {word}");
%%%%%%% Changes from base to side martinvonz#2
-fn example(w: String) {
+fn example(w: &str) {
     println!("word is {w}");
>>>>>>> Conflict 1 of 1 ends
}
```
scott2000 added a commit to scott2000/jj that referenced this issue Nov 17, 2024
Adds a new "snapshot" conflict marker style which returns a series of
snapshots, similar to Git's "diff3" conflict style. The "snapshot"
option uses a subset of the conflict hunk headers as the "diff" option
(it just doesn't use "%%%%%%%"), meaning that the two options are
trivially compatible with each other (i.e. a file materialized with
"snapshot" can be parsed with "diff" and vice versa).

Example of "snapshot" conflict markers:

```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side martinvonz#1
fn example(word: String) {
    println!("word is {word}");
------- Contents of base
fn example(w: String) {
    println!("word is {w}");
+++++++ Contents of side martinvonz#2
fn example(w: &str) {
    println!("word is {w}");
>>>>>>> Conflict 1 of 1 ends
}
```
scott2000 added a commit to scott2000/jj that referenced this issue Nov 17, 2024
Adds a new "ui.conflict-marker-style" config option. The "diff" option
is the default jj-style conflict markers with a snapshot and a series of
diffs to apply to the snapshot. New conflict marker style options will
be added in later commits.

The majority of the changes in this commit are from passing the config
option down to the code that materializes the conflicts.

Example of "diff" conflict markers:

```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side martinvonz#1
fn example(word: String) {
    println!("word is {word}");
%%%%%%% Changes from base to side martinvonz#2
-fn example(w: String) {
+fn example(w: &str) {
     println!("word is {w}");
>>>>>>> Conflict 1 of 1 ends
}
```
scott2000 added a commit to scott2000/jj that referenced this issue Nov 17, 2024
Adds a new "snapshot" conflict marker style which returns a series of
snapshots, similar to Git's "diff3" conflict style. The "snapshot"
option uses a subset of the conflict hunk headers as the "diff" option
(it just doesn't use "%%%%%%%"), meaning that the two options are
trivially compatible with each other (i.e. a file materialized with
"snapshot" can be parsed with "diff" and vice versa).

Example of "snapshot" conflict markers:

```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side martinvonz#1
fn example(word: String) {
    println!("word is {word}");
------- Contents of base
fn example(w: String) {
    println!("word is {w}");
+++++++ Contents of side martinvonz#2
fn example(w: &str) {
    println!("word is {w}");
>>>>>>> Conflict 1 of 1 ends
}
```
scott2000 added a commit to scott2000/jj that referenced this issue Nov 18, 2024
Adds a new "ui.conflict-marker-style" config option. The "diff" option
is the default jj-style conflict markers with a snapshot and a series of
diffs to apply to the snapshot. New conflict marker style options will
be added in later commits.

The majority of the changes in this commit are from passing the config
option down to the code that materializes the conflicts.

Example of "diff" conflict markers:

```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side martinvonz#1
fn example(word: String) {
    println!("word is {word}");
%%%%%%% Changes from base to side martinvonz#2
-fn example(w: String) {
+fn example(w: &str) {
     println!("word is {w}");
>>>>>>> Conflict 1 of 1 ends
}
```
scott2000 added a commit to scott2000/jj that referenced this issue Nov 18, 2024
Adds a new "snapshot" conflict marker style which returns a series of
snapshots, similar to Git's "diff3" conflict style. The "snapshot"
option uses a subset of the conflict hunk headers as the "diff" option
(it just doesn't use "%%%%%%%"), meaning that the two options are
trivially compatible with each other (i.e. a file materialized with
"snapshot" can be parsed with "diff" and vice versa).

Example of "snapshot" conflict markers:

```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side martinvonz#1
fn example(word: String) {
    println!("word is {word}");
------- Contents of base
fn example(w: String) {
    println!("word is {w}");
+++++++ Contents of side martinvonz#2
fn example(w: &str) {
    println!("word is {w}");
>>>>>>> Conflict 1 of 1 ends
}
```
scott2000 added a commit to scott2000/jj that referenced this issue Nov 22, 2024
Adds a new "ui.conflict-marker-style" config option. The "diff" option
is the default jj-style conflict markers with a snapshot and a series of
diffs to apply to the snapshot. New conflict marker style options will
be added in later commits.

The majority of the changes in this commit are from passing the config
option down to the code that materializes the conflicts.

Example of "diff" conflict markers:

```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side martinvonz#1
fn example(word: String) {
    println!("word is {word}");
%%%%%%% Changes from base to side martinvonz#2
-fn example(w: String) {
+fn example(w: &str) {
     println!("word is {w}");
>>>>>>> Conflict 1 of 1 ends
}
```
scott2000 added a commit to scott2000/jj that referenced this issue Nov 22, 2024
Adds a new "snapshot" conflict marker style which returns a series of
snapshots, similar to Git's "diff3" conflict style. The "snapshot"
option uses a subset of the conflict hunk headers as the "diff" option
(it just doesn't use "%%%%%%%"), meaning that the two options are
trivially compatible with each other (i.e. a file materialized with
"snapshot" can be parsed with "diff" and vice versa).

Example of "snapshot" conflict markers:

```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side martinvonz#1
fn example(word: String) {
    println!("word is {word}");
------- Contents of base
fn example(w: String) {
    println!("word is {w}");
+++++++ Contents of side martinvonz#2
fn example(w: &str) {
    println!("word is {w}");
>>>>>>> Conflict 1 of 1 ends
}
```
scott2000 added a commit to scott2000/jj that referenced this issue Nov 22, 2024
Adds a new "git-diff3" conflict marker style option. This option matches
Git's "diff3" conflict style, allowing these conflicts to be parsed by
some external tools that don't support JJ-style conflicts. If a conflict
has more than 2 sides, then it falls back to the similar "snapshot"
conflict marker style.

The conflict parsing code now supports parsing Git-style conflict
markers in addition to the normal JJ-style conflict markers, regardless
of the conflict marker style setting. This has the benefit of allowing
the user to switch the conflict marker style while they already have
conflicts checked out, and their old conflicts will still be parsed
correctly.

Example of "git-diff3" conflict markers:

```
<<<<<<< Side martinvonz#1 (Conflict 1 of 1)
fn example(word: String) {
    println!("word is {word}");
||||||| Base
fn example(w: String) {
    println!("word is {w}");
=======
fn example(w: &str) {
    println!("word is {w}");
>>>>>>> Side martinvonz#2 (Conflict 1 of 1 ends)
}
```
scott2000 added a commit that referenced this issue Nov 23, 2024
Adds a new "ui.conflict-marker-style" config option. The "diff" option
is the default jj-style conflict markers with a snapshot and a series of
diffs to apply to the snapshot. New conflict marker style options will
be added in later commits.

The majority of the changes in this commit are from passing the config
option down to the code that materializes the conflicts.

Example of "diff" conflict markers:

```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side #1
fn example(word: String) {
    println!("word is {word}");
%%%%%%% Changes from base to side #2
-fn example(w: String) {
+fn example(w: &str) {
     println!("word is {w}");
>>>>>>> Conflict 1 of 1 ends
}
```
scott2000 added a commit that referenced this issue Nov 23, 2024
Adds a new "snapshot" conflict marker style which returns a series of
snapshots, similar to Git's "diff3" conflict style. The "snapshot"
option uses a subset of the conflict hunk headers as the "diff" option
(it just doesn't use "%%%%%%%"), meaning that the two options are
trivially compatible with each other (i.e. a file materialized with
"snapshot" can be parsed with "diff" and vice versa).

Example of "snapshot" conflict markers:

```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side #1
fn example(word: String) {
    println!("word is {word}");
------- Contents of base
fn example(w: String) {
    println!("word is {w}");
+++++++ Contents of side #2
fn example(w: &str) {
    println!("word is {w}");
>>>>>>> Conflict 1 of 1 ends
}
```
scott2000 added a commit that referenced this issue Nov 23, 2024
Adds a new "git" conflict marker style option. This option matches Git's
"diff3" conflict style, allowing these conflicts to be parsed by some
external tools that don't support JJ-style conflicts. If a conflict has
more than 2 sides, then it falls back to the similar "snapshot" conflict
marker style.

The conflict parsing code now supports parsing Git-style conflict
markers in addition to the normal JJ-style conflict markers, regardless
of the conflict marker style setting. This has the benefit of allowing
the user to switch the conflict marker style while they already have
conflicts checked out, and their old conflicts will still be parsed
correctly.

Example of "git" conflict markers:

```
<<<<<<< Side #1 (Conflict 1 of 1)
fn example(word: String) {
    println!("word is {word}");
||||||| Base
fn example(w: String) {
    println!("word is {w}");
=======
fn example(w: &str) {
    println!("word is {w}");
>>>>>>> Side #2 (Conflict 1 of 1 ends)
}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants