# Gradle이 jj를 쓰지 못한 사소한 이유

> Clean Markdown view of GeekNews topic #31213. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=31213](https://news.hada.io/topic?id=31213)
- GeekNews Markdown: [https://news.hada.io/topic/31213.md](https://news.hada.io/topic/31213.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-07-07T21:01:48+09:00
- Updated: 2026-07-07T21:01:48+09:00
- Original source: [blog.gradle.org](https://blog.gradle.org/the-petty-reason-we-didnt-end-up-using-jj-at-gradle)
- Points: 1
- Comments: 1

## Topic Body

- Gradle 팀은 Git 호환 VCS인 **Jujutsu(jj)** 를 일상 개발에 도입하려 했지만, Windows용 `gradlew.bat`의 줄바꿈 처리 때문에 채택을 미룸
- 핵심 문제는 `jj`가 **`.gitattributes`의 파일별 EOL 규칙**을 읽지 못해 `*.bat text eol=crlf`를 적용할 수 없다는 점임
- Git은 `gradlew.bat`를 저장소에는 LF로 정규화하고 작업 디렉터리에는 CRLF로 체크아웃하지만, `jj`는 이를 변경으로 인식해 **phantom modification**을 계속 만듦
- `gradlew.bat`를 CRLF 그대로 커밋하고 `*.bat -text`로 바꾸면 우회할 수 있으나, 편집기가 LF로 바꿔도 Git이 자동 복구하지 않아 **Windows 실행 안정성**이 흔들릴 수 있음
- Gradle 팀은 당분간 **`git worktree`** 로 여러 작업 디렉터리를 운영하고, `jj`의 `eol` 지원이 구현되면 다시 시도할 계획임

---

### 채택을 막은 실제 원인: `gradlew.bat`와 `.gitattributes`
- Jujutsu(`jj`)는 Git 백엔드를 사용할 때 커밋과 파일을 Git에 저장하고 기존 Git 저장소와 함께 동작하는 **Git 호환 버전 관리 시스템**임
- `jj`는 스테이징 영역을 없애고, 작업 복사본을 계속 갱신되는 실제 커밋으로 표현함
- 커밋 편집이나 재정렬 뒤에는 후속 커밋을 자동으로 리베이스하고, 충돌은 작업을 멈추기보다 기록에 남길 수 있으며, 작업 로그에서 되돌리기도 가능함
- Gradle 엔지니어가 `git`을 `jj`로 대체하려던 시도는 `jj`의 모델이 아니라 Gradle 프로젝트가 디스크에 놓이는 방식의 작은 세부사항에서 막힘

### Windows에서 `gradlew.bat`가 CRLF를 필요로 함
- 일반적인 Gradle 저장소에는 Gradle Wrapper와 함께 `gradlew.bat`가 포함됨
- `gradlew.bat`는 label과 `goto`를 사용하며, `cmd.exe`는 LF 전용 줄바꿈을 가진 배치 파일을 잘못 처리할 수 있음
- Windows에서 안정적으로 실행하려면 `gradlew.bat`가 **CRLF로 체크아웃**되어야 함
- Gradle 프로젝트는 이 요구를 `.gitattributes`로 강제함

```gitattributes
*.bat text eol=crlf
```

- Git에서는 이 동작이 표준적인 흐름임
  - 파일은 인덱스에 정규화된 형태로 저장됨
  - `eol=crlf` 속성은 체크아웃 시 작업 복사본에 CRLF를 물리화함
  - 편집 내용은 다시 저장될 때 정규화됨

### `jj`의 `.gitattributes` 미지원이 만드는 phantom diff
- `jj`는 `.gitattributes`를 읽지 않아 파일별 줄바꿈 규칙을 적용할 수 없음
- 관련 기능은 오래된 이슈인 [jj-vcs/jj#53](https://github.com/jj-vcs/jj/issues/53)에서 추적되고 있으며, 적어도 `eol` 속성 지원이 논의 중임
- 현재 `jj`에서 쓸 수 있는 수단은 전역 설정인 **`working-copy.eol-conversion`** 뿐임
  - Git의 `core.autocrlf`처럼 모든 파일에 한꺼번에 적용됨
  - `.bat` 파일만 따로 지정할 수 없음
- colocated repo에서는 문제가 즉시 드러남
  - Git은 `gradlew.bat`를 LF로 저장하고 CRLF로 체크아웃함
  - `jj`는 해당 속성을 존중하지 않기 때문에 디스크의 CRLF를 Git의 LF blob 대비 변경으로 기록함
  - 영향을 받는 Gradle 프로젝트에서는 `gradlew.bat`의 **지속적인 가짜 변경**이 생김
- 이 문제는 Gradle 팀만의 사례가 아님
  - 한 [사용자](https://github.com/jj-vcs/jj/issues/53#issuecomment-2832492103)는 여섯 자리 수 파일에서 phantom change가 발생해 `jj` 사용을 포기함

### 제안된 우회책과 한계
- `jj`의 작성자는 이슈에서 non-colocated workspace를 하나의 경로로 제안함
  - 예: Git 작업 복사본 바깥에서 `jj git init --git-repo=&lt;path&gt;` 사용
- Gradle 엔지니어는 이 방식도 문제를 해결하지 못한다고 봄
  - `jj`가 저장된 LF blob에서 `gradlew.bat`를 물리화하면 디스크에는 LF로 내려옴
  - LF `gradlew.bat`는 Windows에서 실행되지 않음
- 전역 설정 `working-copy.eol-conversion = input-output`으로 CRLF를 강제할 수는 있음
  - 하지만 모든 텍스트 파일을 체크아웃 시 CRLF로 다시 쓰기 때문에 별도의 문제가 됨

### 동작하지만 취약한 해결책
- 실제로 동작하는 방법은 `gradlew.bat`를 저장소에 **CRLF 그대로 커밋**하고 정규화를 중단하는 것임
- 규칙은 다음처럼 바뀜
  - 기존: `*.bat text eol=crlf`
  - 변경: `*.bat -text`
- 이렇게 하면 Git과 `jj`가 저장 트리와 작업 복사본 모두에서 CRLF에 동의함
  - 변환이 없어짐
  - phantom diff도 사라짐
  - Windows 실행도 유지됨
- 대신 `*.bat -text`는 `text eol=crlf`가 제공하던 **자동 복구**를 포기함
  - 이전에는 실수로 LF가 들어와도 커밋 시 조용히 다시 정규화됨
  - 이제는 LF가 그대로 남아 `gradlew.bat`가 Windows에서 조용히 깨질 수 있음
- 이 변환은 한 번만 하면 되고, 잘못된 편집은 보통 리뷰에서 잡힐 수 있음
- 그래도 Gradle 팀은 모든 Gradle 프로젝트가 CRLF를 직접 커밋하고 어떤 편집기도 이를 조용히 바꾸지 않는다는 전제에 생태계를 걸기 어렵다고 판단함

### 당분간 유지하는 대안: `git worktree`
- `jj`의 workspace는 하나의 저장소를 공유하는 여러 작업 디렉터리를 제공해, 긴 CI 실행이나 진행 중인 변경이 다음 작업을 막지 않게 해줌
- Gradle 팀은 이미 `git worktree`로 같은 요구를 충분히 처리하고 있음

```bash
git worktree add ../gradle-feature-x feature-x
git worktree add ../gradle-hotfix    hotfix-7.6
```

- 이 방식은 두 개의 체크아웃과 하나의 객체 저장소를 사용함
- stash나 두 번째 clone 없이도 병렬 작업과 장기 유지되는 `main` 체크아웃을 함께 운영할 수 있음
- Gradle 팀은 이 패턴을 수년간 사용해 왔음

### `jj` 재검토 가능성
- 이번 사례가 Gradle 프로젝트에서 `jj` 자체에 대한 최종 판단은 아님
- `jj`의 **operation log**만으로도 더 깊이 검토할 가치가 있음
- `eol` 지원은 해결 가능한 문제로 다뤄지고 있으며, 이슈에는 `gix-filter`를 통한 구현 논의도 있음
- 해당 지원이 들어오면 Gradle 팀은 다시 시도할 계획임
- 그 전까지는 `gradlew.bat` 때문에 Git을 유지하고, `git worktree`가 필요한 역할을 맡음

## Comments



### Comment 61390

- Author: neo
- Created: 2026-07-07T21:01:50+09:00
- Points: 1

###### [Lobste.rs 의견들](https://lobste.rs/s/27dxjg/petty_reason_we_didn_t_end_up_using_jj) 
- Git의 **`autocrlf` 설정**과 뒤이은 **`.gitattributes` 설정**은 처음부터 실수였고 앞으로도 그럴 것이라고 봄  
  버전 관리 시스템이 저장소 안의 파일을 디스크 위 파일과 다르게 저장해서는 안 됨  
  파일 형식을 강제하고 싶다면 린터, 커밋 훅, 병합 훅이 할 일이지 버전 관리 시스템의 역할은 아님
  - 처음부터 실수였다고 보는 쪽에 가깝지만, 지금은 명확히 실수라고 선언하고 특히 **`core.{eol, safecrlf, autocrlf}`** 는 폐기 예정으로 돌려야 한다고 봄  
    줄 끝 변환이 추가된 건 Windows 때문뿐이었고, ␍ 전용은 Git 이전에 이미 죽었으며 Windows만 빼고 모두 ␊ 전용으로 정착했음  
    이제는 실제로 ␍␊를 신경 쓰는 주요 대상이 배치 파일 정도일 듯함  
    모든 언어가 ␍␊를 지원하려고 애쓰지만, 이제는 그것을 요구하거나 생성하는 것은 거의 없음  
    Notepad가 아직도 ␍␊를 기본값으로 쓰거나 그것만 지원한다면, Notepad를 쓰는 사람이 그 결과를 감수해야 함  
    Windows 호환성에서 더 흥미로운 영역은 파일 모드임  
    Git은 Unix 모드를 가져왔고 그 흔적이 남아 있어서, Windows에서 작업하다 보면 의도치 않은 **모드 오류**가 생기기도 함
  - 오히려 반대로, 버전 관리는 이런 일을 덜 할 게 아니라 더 해야 한다고 느낌  
    도구가 충분히 좋아져서 체크아웃할 때 자동으로 공백을 쓰거나, 누군가가 강하게 고집하는 이상하게 구체적인 공백 개수로 맞춰 준다면 탭과 공백 논쟁을 멈출 수 있을 것임  
    더 나아가 버전 관리 시스템이 원문 텍스트가 아니라 **추상 구문 트리(AST)** 를 저장해야 한다고까지 볼 수도 있음  
    지금은 설득하기 어렵겠지만, 이런 방식이 매우 자연스러운 다른 시간선이나 미래도 상상 가능함

- `.bat` 파일을 Windows가 아니면 어디서 실행한다는 건지 모르겠음  
  다른 동작이 조금이라도 바람직한 경우가 어디에 있는지도 의문임

- 필요한 줄 끝 형식 그대로 파일을 커밋하면 안 된다는 논증에는 설득되지 않음  
  핵심은 “누군가 줄 끝을 망가뜨려 커밋하고, 코드 리뷰를 통과해 깨진 스크립트가 들어갈 수 있다”는 것처럼 보임  
  전부 맞지만 그래서 **CI 린트 검사**가 있는 것 아닌가?  
  해당 파일에서 CRLF가 아닌 줄 끝을 스캔하고 발견되면 CI를 실패시키면, 줄 끝을 망가뜨린 수정은 풀 리퀘스트 단계를 통과할 수 없음
  - 이 글의 목적은 Gradle에서의 jj만 다루는 게 아니라, **JVM 생태계**에서 jj를 쓰려는 모든 사람과 프로젝트에 이 문제가 관련된다는 점을 보여주는 것 같음  
    “그냥 CI를 추가하면 된다”는 답은 충분하지 않음  
    관련 프로젝트가 비교적 초보자가 만드는 Minecraft 모드일 수도 있고, 그중 누구든 `gradle.bat`를 깨뜨리고도 알아차리지 못할 수 있음  
    나도 초보자가 시작하기에 CLI Git보다 훨씬 낫다며 **jj**를 대안으로 홍보한 책임이 있음

- `gradlew.bat`에서 `gradlew.ps1`로 옮기는 것, 즉 `cmd.exe`/배치 파일에서 **PowerShell**로 옮기는 선택지는 가능할까?
  - Gradle에는 관여하지 않았지만, 몇 년 전 Leiningen에서 그렇게 해보려 했고 당시에는 완전히 엉망이라 전혀 실용적이지 않았음  
    지금은 MAYBE Windows 설치 기반의 90%가 충분히 최신 PowerShell을 갖췄다고 가정할 수 있을지도 모름  
    하지만 기본 실행 권한을 올려 줘야 하는 경우가 여전히 있고, 기업 환경에서는 잠겨 있을 수도 있음  
    PowerShell을 얼마나 써봤는지는 모르겠지만 “bash가 있으니 `.sh` 파일을 실행하면 된다” 같은 느낌이 아니었음  
    Windows 버전마다 기능 집합이 달라서, `ksh`와 `bash4`를 비교하는 듯한 느낌이었음
