- Notifications
You must be signed in to change notification settings - Fork 834
Gitattributes EOL conversion support #8266
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
base: main
Are you sure you want to change the base?
Conversation
1770764 to e1a1da0 Compare Introduce an API internal to `jj-lib` that future git attributes related features could use to query the git attributes information associated with a file. The design can be found at jj-vcs#7164.
PhilipMetzger left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leaving a small review with some nits
| /// The error type for `gitattributes` operations. | ||
| /// | ||
| /// Errors mostly originate from reading from the store, reading from the file | ||
| /// system, and path conversion. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: move above the derive to be consistent with the rest of the codebase
| let attributes_names = expected_states | ||
| .iter() | ||
| .map(|(name, _)| *name) | ||
| .collect::<Vec<_>>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: should be collect_vec()
| if !metadata.is_file() { | ||
| return Ok(None); | ||
| } | ||
| let file = match std::fs::File::open(&path) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: import std::fs
| // link. | ||
| let metadata = match std::fs::symlink_metadata(&path) { | ||
| Ok(metadata) => metadata, | ||
| Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: import std::io to shorten this
| tree_builder(&test_repo.repo, path) | ||
| .into_iter() | ||
| .map(|tree| tree.id().clone()) | ||
| .collect::<Vec<_>>(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can also be collect_vec()
| tree_builder(&test_repo.repo, path) | ||
| .into_iter() | ||
| .map(|tree| tree.id().clone()) | ||
| .collect::<Vec<_>>(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
| tree_builder(&test_repo.repo, path, contents) | ||
| .into_iter() | ||
| .map(|tree| tree.id().clone()) | ||
| .collect::<Vec<_>>(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
| #[derive(PartialEq, Eq, Debug, Clone)] | ||
| pub(crate) struct EolGitAttributes { | ||
| pub eol: State, | ||
| pub text: State, | ||
| pub crlf: State, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: missing documentation even though parts are probably self-explanatory.
| enum ConvertMode { | ||
| /// Apply EOL conversion on both snapshot and update. Equivalent to | ||
| /// `eol=crlf`. | ||
| InputOutput, | ||
| /// Apply EOL conversion only on snapshot. Equivalent to `eol=lf`. | ||
| Input, | ||
| } | ||
| | ||
| enum EffectiveEolMode { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: add some toplevel doc comment to both enums
| let git_attributes = if self.settings.use_git_attributes { | ||
| // We only read the gitattributes file if necessary to save the cost for users | ||
| // who don't use gitattributes. | ||
| get_git_attributes().await? | ||
| } else { | ||
| EolGitAttributes { | ||
| eol: State::Unspecified, | ||
| text: State::Unspecified, | ||
| crlf: State::Unspecified, | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: move this to a helper since we have two callers
Checklist
If applicable:
CHANGELOG.mdREADME.md,docs/,demos/)cli/src/config-schema.json)Hi Reviewers,
Before you start the actual code review, let's make sure we are on the same page on the scope and requirements of this PR:
local_working_copy. Therefore, external diff tools and external merge tools are out of scope. Thejj diff,jj showandjj file show1 are also out of scope, and will only show the contents directly retrieved fromStorewithout EOL conversion.Storewithout EOL conversion. This is an existing behavior, and this PR won't change that.3GitAttributesthe same way asGitIgnore, because:chain_with_file, which is fine in the snapshot path inlocal_working_copy, but is problematic for the update path inlocal_working_copyand when we implement external diff tools support and external merge tools support. This justifies the design thatGitAttributesexposes one singleGitAttributes::searchAPI apart from initialization.GitIgnoredesign only reads from the disk. However, if the.gitattributesfile is removed from the sparse pattern, we should read the.gitattributesfrom theStore. On update, we should read thegitattributesfile from from theStoreinstead from the disk. On snpashot, we should read from the disk. This justifies the separate abstraction ofFileLoader..gitattributesfile doesn't exist on the disk, but exists in theStore, the.gitattributesfrom theStorewill be used, and vice versa. In case the.gitattributesfile exists in both theStoreand the disk, on snapshot, the file on the disk takes the priority, and on update, the file from theStoretakes the priority.4text,eol,crlf, andcore.eol. Details can be found in the gitattributes doc, and my detailed design doc. You should just focus on the Goals/Requirements section of my verbose design doc. While it can be too complicated for the complete support, and we probably want to get rid of some of the features, please provide guidance on what features we want to keep, and what features we want to get rid of, so that I know how to proceed with this PR.The overview of the design can be found at here and here. You can just take a look at the graph if it's too verbose.
I leave the commit message almost empty so that I don't have to rewrite the commit message before we reach an agreement on the big picture of the design. The same to docs. I know I have introduced new settings and new features that need documentation, but I do want to write them in details after the UX is almost decided. Sorry for the incompleteness, and thanks for the review.
Footnotes
jj diff,jj showandjj file showare more closely related to thediffgitattributes support, which I will open a separate issue.git-lfsmakes use of thediffgitattributes. ↩This behavior exists when we first introduce the
working-copy.eol-conversionsetting, and this PR doesn't change that. The change to this behavior is out of scope of this PR. However, I will open an issue to discuss whether this behavior is expected, and do we want to change that. This can be more of an issue when we consider the filter gitattributes support: should we send contents with conflict markers to the filter process or should we send the different sides of contents to the filter process and materialize the conflict from the filter output? ↩We may want to add conversion hooks when merge happens for
jj. This is more related to themergegitattributes support, which I will open a separate issue.git-lfsmakes use of themergegitattributes. ↩While this follows the gitattributes document, this can result in weird behavior when we try to remove a
.gitattributesfile and snapshot: the removal won't take effect until another snapshot. ↩