Git Autocorrect가 포뮬러 원 드라이버에게 너무 빠른 이유
(blog.gitbutler.com)- Git의
help.autocorrect=1은 오타 명령을 고친 뒤 0.1초만 기다리고 실행해, 사용자가 확인하거나 취소하기에는 사실상 즉시 실행에 가까움 - 이 동작은 Git 기본값이 아니며, 기본 설정에서는 알 수 없는 명령을 실행하지 않고 유사한 명령만 보여준 뒤 종료함
- 2008년 논의에서
help.autocorrect가 불리언이 아니라 decisecond 단위 대기 시간으로 바뀌면서,1이true가 아닌 0.1초 대기로 해석됨 - 현재
help.autocorrect는0, 양수,immediate,prompt,never를 지원하며, 확인 후 실행하려면prompt가 더 안전한 선택임 - Git의 명령 추정은 수정된 Levenshtein distance 기반이며, Scott Chacon은
1과 불리언 문자열을 더 자연스럽게 처리하는 패치를 제안함
git pushy가 git push로 실행되기까지
@dhh가git pushy를 입력했을 때 Git은 이를git push로 추정하고, 실행 전 0.1초만 기다림- 0.1초는 사용자가 명령을 읽고, 맞는지 판단하고, 취소하기에 너무 짧은 시간임
- 이 동작은 Git의 기본 동작이 아니라
help.autocorrect설정이 켜져 있을 때 나타남
기본 동작과 help.autocorrect의 탄생
- Git의 기본 반응은 알 수 없는 명령을 실행하지 않고, 문자열 유사도에 따라 가능한 명령을 보여준 뒤 종료하는 것임
- 예:
git pushy는push가 가장 유사한 명령이라고 표시할 수 있음
- 예:
- 원래 Git은 알 수 없는 명령에 대해 단순히 “Git 명령이 아님”이라고만 응답했음
- 2008년 Johannes Schindelin은 알려진 명령을 훑어 가장 비슷한 명령을 찾고, 가까운 후보가 하나뿐이면 실행하는 작은 패치를 제안함
- Alex Riesen은 이를
help.autocorrect설정으로 제어하는 패치를 냈고, 초기에는 이 설정이 불리언이었음- Git 설정에서 불리언 값은
1을true로 해석할 수 있음 - 그래서 원래 맥락에서는
help.autocorrect=1이 자동 보정을 켜는 값처럼 보일 수 있었음
- Git 설정에서 불리언 값은
1이 0.1초가 된 이유
- Junio Hamano는
help.autocorrect를 단순 불리언이 아니라, 실행 전 기다릴 decisecond 수로 만들자고 제안함- decisecond는 0.1초 단위임
- 사용자가
^C를 누를 기회를 주려는 의도였음
- 이후 받아들여진 패치에서 설정값은 대기 시간으로 바뀜
- 그 결과
help.autocorrect=1은 “기능을 켠다”가 아니라 “1 decisecond, 즉 0.1초 기다린 뒤 실행한다”는 뜻이 됨 - 왜 decisecond가 적절한 시간 단위로 선택됐는지는 논의에 남아 있지 않음
지금 선택할 수 있는 설정값
- Git 문서 기준
help.autocorrect는 다음 값을 지원함0: 기본값이며, 제안 명령만 보여줌- 양수: 지정한 decisecond 뒤 제안 명령을 실행함
immediate: 제안 명령을 즉시 실행함prompt: 제안을 보여주고 실행 여부를 확인함never: 제안 명령을 실행하지도, 보여주지도 않음
- 확인 후 실행을 원한다면
prompt가 대부분의 사용자에게 더 합리적임 - 설정 예시는 다음과 같음
git config --global help.autocorrect prompt
- 이후
git pushy같은 명령을 입력하면push를 실행할지[y/N]로 확인함
Git이 비슷한 명령을 고르는 방식
- Git은 무작정 명령을 추정하지 않고, 어느 지점부터는 너무 멀다고 보고 후보를 내지 않음
- 예시 동작은 다음과 같음
git bass는rebase에 충분히 가까워 실행 확인을 띄움git bassa는 더 이상rebase로 보기 어렵다고 판단해 후보를 실행하지 않음git dm은am과rm을 모두 유사한 명령으로 보여줌git dma는am하나로 매칭됨
- 이 로직은 수정된 Levenshtein distance 알고리듬에 기반함
- 한 문자열을 다른 문자열로 바꾸는 데 드는 문자 편집 비용을 계산함
- 일부 연산은 다른 연산보다 더 비싸게 취급됨
- Git에는 하드코딩된 cutoff가 있어, 알려진 명령과의 비용이 너무 크면 사용자가 크게 잘못 입력한 것으로 보고 추정하지 않음
1 해석을 바꾸려는 패치
help.autocorrect=1은 사람 기준으로 사실상immediate와 같기 때문에, Scott Chacon은1을 0.1초 대기가 아니라 즉시 실행으로 해석하는 작은 패치를 작성함- Junio Hamano는
"1"만 특수 처리하지 말고,yes,no,true,off같은 불리언 문자열 값을 제대로 해석하자고 요청함 - 패치의 두 번째 버전은 이러한 불리언 문자열 처리까지 포함하는 방향으로 진행 중임
- 패치가 반영되면 향후 Git 버전에서
help.autocorrect의 혼동이 줄어들 수 있음
댓글과 토론
Hacker News 의견들
-
70년대에 Hal Finney가 Mattel Intellivision용 BASIC 인터프리터를 2KB ROM에 맞추고 있었는데, 바이트 하나하나가 귀했음
문법 오류를 표시하기 위해 모든 오류 메시지를EH?로 줄였고, 본인은 그걸 꽤 자랑스러워했다는 점이 아직도 웃김EH?는 옛날식 인터랙티브 픽션 게임의 텍스트 파서 응답으로도 잘 어울릴 것 같음
약간 관련해서, 예전 BASIC 변종 중에는PRINT문을?로 쓰는 것도 있었는데, 메모리 절약보다는 REPL에서 타이핑 시간을 줄이려는 목적에 가까웠던 것 같음- 낭비가 심하네.
ed는 모든 오류에?하나만 쓰니 3배 절약됨 - 인터프리터 응답으로
Eh?를 쓴 가장 이른 사례는 RAND의 JOSS에서 봤음
[https://en.wikipedia.org/wiki/JOSS#/media/File:JOSS_Session....](https://en.wikipedia.org/wiki/JOSS#/media/File:JOSS_Session.jpg)
https://en.wikipedia.org/wiki/JOSS
메모리는 약 5KB였지만, Intellivision과 비교하면 기계 무게가 약 5,000파운드였음 - 꽤 멋지다. Hal이 그 시절 개인용 컴퓨터 해커였다는 건 몰랐고, Hal 얘기를 들으면 항상 Bitcoin이 떠오름
- Wordle 파생 게임인 xordle을 운영하는데, 한 보드에 Wordle 퍼즐 두 개가 들어감
그래서 어떤 단어를 추측해 5글자가 전부 초록색이 됐는데도 두 정답 중 어느 것도 아닐 수 있음. 그럴 때 오른쪽에 그냥Huh?라고 뜨게 했는데, 사람들이 그 부분을 좋아함
-
근본 원인은 설정 이름이 나쁘게 지어진 것임
원래 설정이help.autocorrect_enabled처럼 불리언스럽게 지어졌다면, 정수값을 받아 0.1초 단위로 해석하자는 요청은 말이 안 됐을 것임. 대신help.autocorrect_accept_after_dsec같은 다른 설정이 필요했을 테고,dsec는 워낙 생소해서 쓰는 사람이라면 찾아볼 수밖에 없었을 것임
코드 리뷰에서 늘 이런 걸 요구함. 모호할 수 있으면 변수명에 단위가 들어가야 함. 예를 들어int timeout은int timeout_msec가 되어야 함
설정 이름에서는 이게 100배 더 중요함. 설정은 공개 인터페이스의 일부라서 나중에 바꿀 수 없기 때문임- 같은 생각임. 다만 이게 타입 시스템으로 밀려 들어갈 때는 아직도 고민됨
C++ 맥락에서 대략적인 기준은void FooBar(std::chrono::milliseconds timeout);은 괜찮음. 함수 시그니처라서 볼 때 타입도 같이 보이기 때문임
하지만 변수에서timeout은 안 괜찮음. 99%의 경우 실제 사용은auto timeout = gl_timeout;이나FooBar(timeout);처럼 보게 됨
C++에서auto를 흔히 쓰다 보니, 중요할 때 정확한 타입을 추적하는 게 꽤 귀찮아짐
C++ 작업할 때 IDE나 언어 서버 붙은 편집기를 쓰지만, 5초마다 마우스를 올려 타입을 확인할 시간은 없음 - 맞고, 여기에 평생 문자 그대로 0번 써본 시간 단위인 0.1초 단위(decisecond) 를 쓴 게 상황을 더 악화시킴
“1ms 뒤에 실행합니다”라는 메시지를 보면 바로 설정을 확인했을 것임 - 그러다 보면 systemd처럼
TimoutSec=60도 쓰고TimeoutSec=1min도 쓸 수 있는 상태가 됨
차라리 단위를 이름에 안 넣는 편이 나았을 거라고 봄. 다만 그 특이한 경우만 빼면 전적으로 동의함 - 그렇게 하긴 하지만, 헝가리안 표기법 냄새가 난다는 생각을 떨치기 어려움
지금까지 찾은 가장 나은 대안은 값에 단위를 받는 것임. 예를 들어5 seconds나5s를 허용하고, 그냥1은 잘못된 값으로 처리하는 방식임 - 코드 리뷰에서 그런
int사용 자체를 표시하고, 대신 단위를 제대로 전달하는 값 클래스를 권함
예를 들어Second(2)나Millisecond(2000)같은 식임
물론 이건 언어의 기능과 관습에 따라 달라짐
- 같은 생각임. 다만 이게 타입 시스템으로 밀려 들어갈 때는 아직도 고민됨
-
xmobar도 비슷하지만 더 문제 있는 곳에서 0.1초 단위를 씀. 각 섹션을 얼마나 자주 새로고침할지 선언하는 설정임
예제 설정의 숫자가 너무 작아서 밀리초일 리는 없어 보이고, 그래서 합리적으로 초라고 가정하게 만들고, 결과적으로 의도보다 10배 자주 명령을 실행하게 됨
이 문제에서 비롯된 우발적 부하 급증을 여러 번 봤음 -
꽤 나쁜 설계로 보임
다만 1번은 글을 잘못 읽은 결과였고, “이전 값”은 git에 존재한 적이 없었음- 기존 설정값을 조용히 다른 의미로 재해석하는 변경, 예를 들어
1=true를1=0.100ms 확인 지연으로 바꾸는 일은 거의 항상 피해야 함. 기존 값이 있었다면 지우고 싶겠지만, 설정 이름도 바꾸는 게 맞음 help.autocorrect의 설정 인자가 시간이고, 대부분 사용자에게 표준적이지 않은 단위로 측정된다는 건 그냥 나쁨. 활성화는 불리언으로 받고, 확인 시간은 소수값으로 조절하게 해줘야 함
- 1번에 대해서는 시간순서를 오해한 것 같음
그 변경은 2008년에, 해당 옵션을 불리언으로 추가하려는 초기 패치의 코드 리뷰 중에 일어났고, 메인 git 트리에 커밋되기 전이었음 - “설계”라고 하면 의도적인 넓은 맥락의 계획을 떠올리게 됨
이건 설계라기보다 유기적으로 자라난 곁가지에 가까움
- 기존 설정값을 조용히 다른 의미로 재해석하는 변경, 예를 들어
-
이건 기능 과잉(creeping featurism) 의 좋은 예라고 봄
좋게 봐도 불필요한 복잡성을 들여오고, 나쁘게 보면 거기에 의존하는 사람들이 자기가 뭘 하는지 덜 주의하게 만듦- git이 딱 그렇다. 우아한 자료 구조 위에 세월이 지나며 쌓인 불필요한 쓰레기 층이 여러 겹 덮여 있음
- 왜 자동 실행을 허용하고 싶어 하는지 이해가 안 됨
위쪽 화살표를 누르고 실수를 고치는 게 그렇게 어려운가? “대충 맞는” 것을 자동으로 실행하는 건 의도하지 않은 일을 하게 되는 레시피임
-
실수는 여기 있었음. 기존 설정을 다른 의미로 재활용하지 말고 새 설정을 추가했어야 함
help.autocorrect는 켜기/끄기,help.autocorrect.milliseconds는 얼마나 기다릴지로 나눴어야 함
다른 시스템에도 비슷한 실수가 있음. 예를 들어 MySQL의innodb_flush_log_at_trx_commit은 0이면 비활성, 1이면 활성이고, 2가 나중에 특수한 의미로 추가됨- 진짜 문제는
1이 무엇을 뜻하는지 추측하려는 타입 없는 설정 언어임
이제1은 불리언 참으로 두면서 다른 정수는 0.1초 단위로 만들며 더 깊이 빠지고 있음
YAML의 악명 높은no == false와 같은 의심스러운 논리임 - 어디에 쓰는 게 가장 좋을지는 모르겠지만, 0.1초도 말이 안 되는 값은 아님
기록상 가장 빠른 무작위 반응 시간이 1.5ds 정도이고, 우연히 평균 게이머 반응 시간도 그쯤이지만, 비무작위 반응은 훨씬 빠를 수 있음. 예를 들어 박자에 맞춰 반응하는 경우가 그렇다
그렇게 빠르게 가고 싶다면 가능하고, 호출 속도도 비교적 안정적이어야 함. 대략 몇 밀리초 수준일 것임
- 진짜 문제는
-
1년 전에 자동 수정 기능을 켜고 3초로 설정했는데, 두 가지를 느꼈음
- 위험한 동작과 안전한 동작을 구분하지 않음
- 잘못 입력한 명령으로 셸 히스토리를 오염시킴
이 글을 읽고 나니 1년 만에 끌 만큼의 계기가 됐음
- 차라리 자주 하는 오타에는 별칭(alias) 을 만드는 편이 나음
물론 셸 히스토리를 “오염”시키긴 함 - 2번에 대해서는, 실제 오염자는 본인임
히스토리를 거슬러 올라가서, 어차피 동작한다는 이유로 마지막의 잘못된 명령을 그대로 다시 쓰더라도 마찬가지임
-
0.1초 단위는 정말 특이한 선택임
지연 시간은 밀리초나 초로 지정하는 편이 낫고, 둘 다 컴퓨팅에서 훨씬 흔하게 쓰임- 잠깐 꽤 헷갈렸음.
deciseconds가 git 전용 단위로, “결정을 내리는 데 필요한 초” 같은 decision-seconds를 뜻하는 줄 알았음 xD
참고로 영어가 모국어는 아니지만, 미터법을 쓰는 문명화된 지역 출신임 - 사람의 반응 시간에는 괜찮지만 흔치 않은 단위임
0초와 1초의 차이는 뭔가를 기다릴 때 눈에 띄게 길지만, n밀리초와 n+1밀리초의 차이는 유용하게 쓰기엔 너무 미세함
- 잠깐 꽤 헷갈렸음.
-
반응 시간은 자극 종류에 따라 다르고, 청각이 시각보다 약간 빠르며 촉각은 그보다 조금 더 빨라 90~180ms 정도임
그래서 git이 오류 메시지 대신 한 대 때려준다면, 간신히 반응할 시간이 있을지도 모름- 그 때리는 장치가 맞는 느낌을 주려면 관성을 만들어야 함
10ms면 그게 충분할까?
- 그 때리는 장치가 맞는 느낌을 주려면 관성을 만들어야 함
-
이 글을 읽으니 소프트웨어 고고학과 “프로그래머 고고학자”라는 말이 떠오름
뒤의 개념은 Vernor Vinge에게 감사해야 함- 이건 정말 거슬리는 지점임. 해커들이 “소프트웨어 고고학” 개념을 그 책이 도입했다고 띄워서, 『A Fire Upon The Deep』을 읽는 게 무척 기대됐음
그런데 그 개념은 프롤로그에서 잠깐 암시되고, 그 뒤로는 전혀 없음. 고고학의 효과는 줄거리와 관련 있지만, “소프트웨어 고고학자”는 서사에서 의미 있는 인물이 아니었음
낚였다는 느낌이 들었음 - 소프트웨어 고고학의 위험은 결국 분석(糞石) 을 발견하게 된다는 점이라고 말하곤 함
- 지금 나를 “늙었다”고 부르는 것 같은 느낌을 지울 수 없음
- 이건 정말 거슬리는 지점임. 해커들이 “소프트웨어 고고학” 개념을 그 책이 도입했다고 띄워서, 『A Fire Upon The Deep』을 읽는 게 무척 기대됐음