2016-12-30 122 views
4

我正在開發一個預先提交的鉤子來重新格式化代碼,並且通常它可以工作;它會重新格式化任何分階段文件,並且結果提交包含所需的重新格式化代碼。瞭解git commit - 只有和預先提交的鉤子

但是,它不能很好地與git commit --only(這是JetBrains IDE使用的變體)很好地玩,我試圖理解爲什麼。 /的git commit --only和組合pre-commit鉤子導致不希望的索引工作樹狀態,如在下面的事件序列描述:

如果我使用格式錯誤的變化小到一個文件中,然後運行git status ,這是我所看到的:

On branch master 
Your branch is up-to-date with 'origin/master'. 
Changes not staged for commit: 
    (use "git add <file>..." to update what will be committed) 
    (use "git checkout -- <file>..." to discard changes in working directory) 

    modified: file.php 

no changes added to commit (use "git add" and/or "git commit -a") 

如果我然後提交使用git commit --only -- file.php,預提交鉤子運行,並改變和重新格式化file.php承諾。

但是,如果我然後再次運行git status,這是結果(箭頭標註礦):

On branch master 
Your branch is ahead of 'origin/master' by 1 commit. 
    (use "git push" to publish your local commits) 
Changes to be committed: 
    (use "git reset HEAD <file>..." to unstage) 

    modified: file.php <-- contains original change, improperly formatted 

Changes not staged for commit: 
    (use "git add <file>..." to update what will be committed) 
    (use "git checkout -- <file>..." to discard changes in working directory) 

    modified: file.php <-- contains original change, properly formatted (per the most recent commit) 

哪裏上演新變化和來自的工作樹的變化?

有人能夠準確解釋git commit --only如何與索引交互以產生上面顯示的結果 - 甚至更好,是否有辦法讓我的pre-commit鉤子與它很好地搭配?

我的理解是,git commit --only與工作樹中的文件版本一起工作,所以我嘗試從預提交鉤子中刪除git add步驟以查看將會發生什麼,並導致格式不正確的版本該文件被提交,並在工作樹中正確格式化(這符合我對標準git commit的期望,但我不確定在git commit --only的上下文中會發生什麼)。

我知道使用clean過濾器重新格式化代碼的可能性,而不是預先提交的鉤子,但這種方法引入了一些情景複雜情況,如果可能的話很好避免。

說明:此問題與Phpstorm and pre commit hooks that modify files有關,但重點在於解決git commit --only上下文中的問題。此外,JetBrains似乎也沒有解決這個問題,正如在該問題的公認答案中所表明的那樣。

+0

關於更多關於預提交對git commit行爲的影響:https://stackoverflow.com/a/7230886/6309 – VonC

回答

5

從一個版本的Git到另一個版本的確切細節有所不同,有些人 - 我不是說JetBrains人員是其中的一員,因爲我不知道 - 試圖繞過Git做的事情,並且在過程,把事情弄糟,以至於無法解決問題,或者解決方法是依賴於Git版本。然而,在這些混帳掛鉤的主要思路都是一樣的:

  • 指數包含提交到化妝,和
  • 工作樹包含工作樹。

這兩個不一定是同步的,當你第一次運行git commit,如果您將文件添加到git commit命令,無論使用哪種--only--include,Git會必須再作指數,它可以從不同常規普通指數。所以現在我們結束一個環境變量,GIT_INDEX_FILE,設置爲一個新的臨時索引的路徑。 因爲所有Git命令都會自動遵守環境變量,所以預提交鉤子將使用臨時索引的文件,並且git write-tree將使用臨時索引的文件。

當然,任何失敗尊重臨時索引或潛在的,這取決於--include VS --only,只是使用的內容,工作樹會得到錯誤的答案。

即使有尊重環境變量的程序,仍然存在問題。假設我們有一個文件 - 我們稱它爲test,因爲這是它的目的 - 最初包含「headders」,並且匹配當前(HEAD)提交。現在我們在工作樹中修改它以包含「索引器」並運行git add testtest的索引版本因此讀取「索引器」。現在我們在工作樹中再次修改它,以包含「工作者」,並運行git commit --only testgit commit --include test

我們肯定知道應該進入新提交的內容:它應該是包含workvers的測試版本,因爲我們特別告訴Git提交工作樹版本。但是索引和工作樹之後應該剩下什麼?這是否取決於我們是否使用--include vs --only?我不知道該怎麼考慮這裏的「正確」答案!我可以告訴你的是,當我之前使用Git進行實驗時,之後往往會包含workvers(無論是在索引中還是在工作樹中)。也就是說,臨時索引的版本成爲普通索引的版本,並且工作樹文件未被觸及。 (如果你有操縱索引和/或工作樹的Git鉤子,你將能夠撬開「將索引複製到保存索引,然後複製回來」和「將索引複製到temp-指數,然後用溫度指數」。)


這是實際執行一次,當我在測試各種行爲,但它可能是實際執行已經改變了一點。例如,Git可以將「正常」索引保存在一個臨時文件中,然後替換正常索引,這樣GIT_INDEX_FILE就是而不是。再次,它可能取決於--include--only

請注意,git commit -a也可能使用臨時索引,或不使用臨時索引。我相信這個行爲在Git 1.7和Git 2.10之間有所變化,這是基於在另一個窗口中運行git status的結果,同時仍然在運行git commit -a的窗口中編輯提交消息。

3

我遇到了同樣的問題。這是我從Jetbrains dev Dmitriy Smirnov獲得的解決方案。


git commit --only用於由於以下幾個原因:

  1. Git的階段,不支持 - https://youtrack.jetbrains.com/issue/IDEA-63391
  2. 它允許進行部分提交 - 提交單個文件。這對於支持IDE中的更改列表至關重要。

目前無法改變行爲。

鑑於如下(紅寶石)的pre-commit鉤子:

`git status --porcelain`.lines do |line| 
    changed_file = line.split(' ', 2)[1].strip() 
    if (File.extname(changed_file).downcase() == '.java') 
     system "java -jar bin/google-java-format-1.4-all-deps.jar --aosp --replace #{changed_file}" 
     system "git add #{changed_file}" 
    end 
end 

添加post-commit鉤:

git update-index -g 

參見

https://youtrack.jetbrains.com/issue/IDEA-81139#comment=27-295117