1 पॉइंट द्वारा GN⁺ 9 시간 전 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Gradle टीम Git-compatible VCS Jujutsu(jj) को रोज़मर्रा के development में अपनाना चाहती थी, लेकिन Windows के लिए gradlew.bat में line ending handling की वजह से अपनाने को टाल दिया
  • मुख्य समस्या यह है कि jj .gitattributes के per-file EOL rules नहीं पढ़ पाता, इसलिए *.bat text eol=crlf लागू नहीं कर सकता
  • Git gradlew.bat को repository में LF के रूप में normalize करता है और working directory में CRLF के रूप में checkout करता है, लेकिन jj इसे change मानकर लगातार phantom modification बनाता रहता है
  • gradlew.bat को CRLF के साथ ही commit करके और *.bat -text में बदलकर workaround किया जा सकता है, लेकिन अगर editor इसे LF में बदल दे तो Git अपने आप recover नहीं करेगा, जिससे Windows execution stability डगमगा सकती है
  • Gradle टीम फिलहाल कई working directories चलाने के लिए git worktree इस्तेमाल करेगी, और jj में eol support implement होने पर फिर से कोशिश करने की योजना है

अपनाने में असली अड़चन: gradlew.bat और .gitattributes

  • Jujutsu(jj) Git backend इस्तेमाल करते समय commits और files को Git में store करता है और मौजूदा Git repositories के साथ काम करने वाला Git-compatible version control system है
  • jj staging area को हटा देता है और working copy को लगातार update होने वाले वास्तविक commit के रूप में दर्शाता है
  • commit edit या reorder करने के बाद यह बाद के commits को अपने आप rebase करता है, conflicts को काम रोकने के बजाय history में रखा जा सकता है, और operation log से वापस भी लौटा जा सकता है
  • Gradle engineer की git को jj से replace करने की कोशिश jj के model की वजह से नहीं, बल्कि Gradle project के disk पर रखे जाने के तरीके की एक छोटी detail पर अटक गई

Windows पर gradlew.bat को CRLF चाहिए

  • सामान्य Gradle repository में Gradle Wrapper के साथ gradlew.bat शामिल होता है
  • gradlew.bat label और goto का इस्तेमाल करता है, और cmd.exe सिर्फ LF line endings वाली batch file को गलत तरीके से process कर सकता है
  • Windows पर भरोसेमंद तरीके से चलाने के लिए gradlew.bat का CRLF के रूप में checkout होना ज़रूरी है
  • Gradle project इस requirement को .gitattributes से enforce करता है
*.bat text eol=crlf
  • Git में यह behavior standard flow है
    • file index में normalized form में store होती है
    • eol=crlf attribute checkout के समय working copy में CRLF materialize करता है
    • edits दोबारा save होने पर फिर normalize हो जाते हैं

jj में .gitattributes support न होने से बनने वाला phantom diff

  • jj .gitattributes नहीं पढ़ता, इसलिए per-file line ending rules apply नहीं कर सकता
  • संबंधित feature पुराने issue jj-vcs/jj#53 में track हो रहा है, और कम-से-कम eol attribute support पर चर्चा चल रही है
  • अभी jj में उपलब्ध साधन केवल global setting working-copy.eol-conversion है
    • Git के core.autocrlf की तरह यह सभी files पर एक साथ apply होता है
    • केवल .bat files को अलग से specify नहीं किया जा सकता
  • colocated repo में समस्या तुरंत दिखती है
    • Git gradlew.bat को LF के रूप में store करता है और CRLF के रूप में checkout करता है
    • jj उस attribute का सम्मान नहीं करता, इसलिए disk पर मौजूद CRLF को Git के LF blob की तुलना में change के रूप में record करता है
    • प्रभावित Gradle projects में gradlew.bat में लगातार fake changes बनते हैं
  • यह समस्या सिर्फ Gradle टीम का case नहीं है
    • एक user ने छह अंकों की संख्या जितनी files में phantom changes आने के कारण jj इस्तेमाल करना छोड़ दिया

सुझाए गए workarounds और सीमाएँ

  • jj के author ने issue में non-colocated workspace को एक रास्ते के रूप में suggest किया
    • उदाहरण: Git working copy के बाहर jj git init --git-repo=<path> इस्तेमाल करना
  • Gradle engineer के मुताबिक यह तरीका भी समस्या हल नहीं करता
    • जब jj stored LF blob से gradlew.bat materialize करेगा, तो disk पर LF आएगा
    • LF gradlew.bat Windows पर execute नहीं होता
  • global setting working-copy.eol-conversion = input-output से CRLF force किया जा सकता है
    • लेकिन यह checkout के समय सभी text files को CRLF में फिर से लिख देता है, इसलिए अलग समस्या बन जाता है

चलने वाला लेकिन कमजोर समाधान

  • वास्तव में काम करने वाला तरीका है gradlew.bat को repository में CRLF के साथ ही commit करना और normalization बंद करना
  • rule इस तरह बदलता है
    • मौजूदा: *.bat text eol=crlf
    • बदला हुआ: *.bat -text
  • ऐसा करने पर Git और jj storage tree और working copy दोनों में CRLF पर सहमत हो जाते हैं
    • conversion खत्म हो जाता है
    • phantom diff भी गायब हो जाता है
    • Windows execution भी बनी रहती है
  • इसके बदले *.bat -text, text eol=crlf से मिलने वाली automatic recovery छोड़ देता है
    • पहले गलती से LF आ भी जाए तो commit के समय चुपचाप फिर normalize हो जाता था
    • अब LF वैसे ही रह जाएगा और gradlew.bat Windows पर चुपचाप टूट सकता है
  • यह conversion केवल एक बार करना होता है, और गलत edits आम तौर पर review में पकड़े जा सकते हैं
  • फिर भी Gradle टीम ने माना कि पूरे ecosystem को इस assumption पर दांव पर लगाना मुश्किल है कि सभी Gradle projects सीधे CRLF commit करेंगे और कोई editor इसे चुपचाप नहीं बदलेगा

फिलहाल जारी रहने वाला विकल्प: git worktree

  • jj का workspace एक repository share करने वाली कई working directories देता है, ताकि लंबी CI runs या in-progress changes अगला काम न रोकें
  • Gradle टीम पहले से ही git worktree से इसी जरूरत को पर्याप्त रूप से संभाल रही है
git worktree add ../gradle-feature-x feature-x
git worktree add ../gradle-hotfix    hotfix-7.6
  • यह तरीका दो checkouts और एक object store इस्तेमाल करता है
  • stash या दूसरे clone के बिना भी parallel work और लंबे समय तक maintained main checkout साथ-साथ चलाए जा सकते हैं
  • Gradle टीम इस pattern को कई सालों से इस्तेमाल कर रही है

jj पर दोबारा विचार की संभावना

  • यह case Gradle project में jj खुद के बारे में अंतिम फैसला नहीं है
  • jj का operation log अकेला भी गहराई से review करने लायक है
  • eol support को हल हो सकने वाली समस्या माना जा रहा है, और issue में gix-filter के जरिए implementation पर चर्चा भी है
  • वह support आने पर Gradle टीम फिर से कोशिश करने की योजना रखती है
  • तब तक gradlew.bat की वजह से Git बना रहेगा, और git worktree जरूरी भूमिका निभाएगा

1 टिप्पणियां

 
GN⁺ 9 시간 전
Lobste.rs की रायें
  • मुझे लगता है कि Git की autocrlf setting और उसके बाद की .gitattributes setting शुरू से ही गलती थीं और आगे भी रहेंगी
    version control system को repository के अंदर मौजूद file को disk पर मौजूद file से अलग तरह से store नहीं करना चाहिए
    अगर file format enforce करना है, तो यह linter, commit hook, merge hook का काम है, version control system की भूमिका नहीं

    • मैं भी इसे शुरू से गलती मानने वाली तरफ ज्यादा हूं, लेकिन अब इसे साफ तौर पर गलती घोषित करना चाहिए और खासकर core.{eol, safecrlf, autocrlf} को deprecated कर देना चाहिए
      line ending conversion सिर्फ Windows की वजह से जोड़ा गया था, ␍-only Git से पहले ही मर चुका था, और Windows को छोड़कर सब ␊-only पर settle हो गए थे
      अब असल में ␍␊ की चिंता करने वाली मुख्य चीज शायद batch files ही होंगी
      सभी languages ␍␊ support करने की कोशिश करती हैं, लेकिन अब लगभग कोई भी इसे require या generate नहीं करता
      अगर Notepad आज भी ␍␊ को default के रूप में इस्तेमाल करता है या सिर्फ वही support करता है, तो Notepad इस्तेमाल करने वाले को उसके नतीजे झेलने चाहिए
      Windows compatibility में ज्यादा दिलचस्प क्षेत्र file mode है
      Git ने Unix modes अपनाए थे और उनके निशान बचे हुए हैं, इसलिए Windows पर काम करते समय कभी-कभी अनचाहे mode errors पैदा हो जाते हैं
    • बल्कि इसके उलट, मुझे लगता है कि version control को ऐसे काम कम नहीं, बल्कि ज्यादा करने चाहिए
      अगर tools इतने अच्छे हो जाएं कि checkout करते समय अपने-आप spaces इस्तेमाल करें, या किसी के जोर देकर चाहने वाले अजीब तरह से specific spaces की संख्या के हिसाब से format कर दें, तो tabs और spaces की बहस बंद हो सकती है
      और आगे बढ़कर यह भी कहा जा सकता है कि version control system को raw text के बजाय abstract syntax tree (AST) store करना चाहिए
      अभी इसे मनवाना मुश्किल होगा, लेकिन कोई दूसरी timeline या भविष्य imagine किया जा सकता है जहां यह तरीका बहुत natural लगे
  • समझ नहीं आता कि .bat file को Windows के अलावा कहां execute किया जाएगा
    यह भी सवाल है कि थोड़ा भी अलग behavior कहां desirable होगा

  • मैं इस दलील से सहमत नहीं हूं कि file को जरूरी line ending format में ही commit नहीं करना चाहिए
    मुख्य बात यह लगती है कि “कोई line endings खराब करके commit कर सकता है, code review पास हो सकता है और टूटी हुई script आ सकती है”
    यह सब सही है, लेकिन इसी लिए तो CI lint check होते हैं, है ना?
    उस file में non-CRLF line endings scan करें और मिलते ही CI fail कर दें, तो line endings खराब करने वाला change pull request stage पार नहीं कर पाएगा

    • इस लेख का मकसद शायद सिर्फ Gradle में jj की बात करना नहीं है, बल्कि यह दिखाना है कि JVM ecosystem में jj इस्तेमाल करना चाहने वाले सभी लोगों और projects के लिए यह समस्या relevant है
      “बस CI जोड़ दो” वाला जवाब काफी नहीं है
      संबंधित project किसी relatively beginner द्वारा बनाया गया Minecraft mod भी हो सकता है, और उनमें से कोई भी gradle.bat को तोड़कर भी notice न करे
      मेरी भी जिम्मेदारी है क्योंकि मैंने beginners के लिए CLI Git से कहीं बेहतर बताते हुए jj को alternative के तौर पर promote किया है
  • क्या gradlew.bat से gradlew.ps1 पर जाना, यानी cmd.exe/batch file से PowerShell पर जाना, एक option हो सकता है?

    • मैं Gradle से जुड़ा नहीं रहा, लेकिन कुछ साल पहले Leiningen में ऐसा करने की कोशिश की थी और उस समय यह पूरी तरह गड़बड़ था, बिल्कुल practical नहीं था
      अब MAYBE यह मान सकते हैं कि Windows install base के 90% के पास sufficiently recent PowerShell है
      लेकिन default execution permission बढ़ानी पड़ने के cases अब भी हैं, और corporate environments में यह locked down भी हो सकता है
      पता नहीं आपने PowerShell कितना इस्तेमाल किया है, लेकिन यह “bash है, तो .sh file चला दो” जैसा feel नहीं था
      Windows versions के हिसाब से feature set अलग था, जैसे ksh और bash4 की तुलना कर रहे हों