[英]automatically stash save/pop changes on git rebase?
我的git工作流程經常使用rebase。 我總是獲取上游更改(我分叉的主要倉庫),然后合並到我的分支,然后rebase刪除無用(對我來說:D)合並提交和樹分割。
這個工作流程中的一件事讓我煩惱的是:
$ git rebase upstream/master
Cannot rebase: You have unstaged changes.
Please commit or stash them.
$ git stash
Saved working directory and index state WIP on cc: abc1234 Merge remote-tracking branch 'upstream/master' into local_branch
HEAD is now at abc1234 Merge remote-tracking branch 'upstream/master' into local_branch
$ git rebase upstream/master
First, rewinding head to replay your work on top of it...
Applying: awesome code change
$ git stash pop
所以這里我們有4個命令,1 =失敗的rebase,2 = stash,3 = rebase,4 = stash pop。 除了3之外的任何東西都是無意識的工作
所以,問題是:最推薦的自動化方法是什么? 每次都運行git stash / rebase / pop的別名? 一些git配置強制rebase隱藏或將其視為另一個提交后再重新應用? 別的什么?
編輯:從Git版本1.8.4開始,但是在Git版本2.0.1中修復了一個重要的漏洞, git rebase
現在有了--autostash
。 您可以將git rebase
配置為默認使用--autostash
,使用git config --global rebase.autoStash true
。 請注意文檔中的以下句子:
但是,謹慎使用:成功重組后的最終存儲應用程序可能會導致非平凡的沖突。
(我仍然喜歡提交。)
它可能會幫助你意識到git stash
實際上只是git commit
(以更復雜的形式,首先提交索引,然后是工作樹 - 當你應用存儲時,你可以保持索引和工作樹的分離,或將它們組合成工作樹的變化)。
使存儲特殊的是它所提交的提交 - 使用-u
或-a
,甚至三個提交 - 以不尋常的形式(作為合並提交並非真正合並)而不是放在任何分支上(相反,特殊的refs/stash
引用用於保留和查找它們)。
由於它們不在分支上,因此rebase
不會觸及它們,並且在您的工作流程中,它是將工作樹更改帶入您的新工作樹的git stash pop
。 但是,如果您在分支上進行自己的(正常)提交,並且使用rebase並包含該提交,則此正常提交將與其他提交一起進行重新設置。 我們馬上就會遇到最后一個問題; 現在,讓我們把它畫出來,作為一系列提交(或不做)的提交:
... do some work ...
... make some commits ...
... more work ...
... do something that causes upstream/master to update, such as git fetch upstream
$ git stash
在這一點上,這是你擁有的:
... - o - * - A - B - C <-- HEAD=master
\ |\
\ i-w <-- stash
\
@-@-@ <-- upstream/master
在這里, A
, B
和C
是你的提交(我假設你已經做了3),所有這些都在分支master
身上。 掛起提交C
的iw
是你的存儲,它不在分支上,但仍然是一個兩次提交的“git stash包” ,實際上附加到你的最新提交( C
)。 @
commits(可能只有一個)是新的上游提交。
(如果你沒有提交,你的stash-bag會掛起commit *
,你當前的分支指向commit *
,所以git rebase
除了向前移動你當前的分支指針之外別無其他工作。一切都運行相同,在這種情況下,但我會假設有一些提交。)
現在你運行git rebase upstream/master
。 這會將您的提交復制到新的提交,使用新的ID和新的父ID,以便它們位於最后一個@
頂部。 存儲袋不會移動,因此結果如下所示:
... - o - * - A - B - C [abandoned, except for the stash]
\ |\
\ i-w <-- stash
\
@-@-@ <-- upstream/master
\
A'-B'-C' <-- HEAD=master
你現在使用git stash pop
,它將i / w內容恢復為工作樹更改,刪除stash
標簽(更准確地說,彈出它以便stash@{1}
,如果存在,現在是stash
,依此類推) 。 這釋放了對原始A - B - C
鏈的最后引用,意味着我們也不需要iw
位,這讓我們重新繪制這個更簡單:
... - @ <-- upstream/master
\
A'-B'-C' <-- HEAD=master plus work tree changes
現在讓我們畫出如果不是git stash save
,你只需要執行git commit -a
(或git add
和git commit
without -a)來創建實際的提交D
你從:
... - o-*-A-B-C-D <-- HEAD=master
\
@-@-@ <-- upstream/master
現在你git rebase upstream/master
,它復制A
到D
把它們放在最后一個@
的末尾,你有這個:
... - o-*-@-@-@ <-- upstream/master
\
A'-B'-C'-D' <-- HEAD=master
唯一的問題是你有這個不需要的額外提交D
(好吧, D'
現在),而不是未提交的工作樹更改。 但是通過git reset
來退回一次提交,這很簡單。 我們可以使用--mixed
reset-默認 - 來重新設置索引(暫存區域),以便“取消添加”所有文件,或者如果你想讓它們保持git add
-ed, --soft
reset。 (不會影響生成的提交圖,只有索引狀態不同。)
git reset --mixed HEAD^ # or leave out `--mixed` since it's the default
這是看起來像:
... - o-*-@-@-@ <-- upstream/master
\
A'-B'-C' <-- HEAD=master
\
D' [abandoned]
您可能認為這是低效的,但是當您使用git stash
您實際上至少進行了兩次提交,然后您在git stash pop
時放棄這些提交。 真正的區別在於,通過臨時,非發布提交,您可以自動重新生成。
git有一個通用的規則:做大量的臨時提交,隨時保存你的工作。 你可以隨時將它們改掉。 也就是說,而不是這個:
... - * - A - B - C <-- mybranch
其中A
, B
和C
是完美的,最后提交在提交*
(來自其他人或早期發布的東西)上,請執行以下操作:
... - * - a1 - a2 - b1 - a3 - b2 - a4 - b3 - c1 - b4 - c2 - c3
其中a1
是A
的初始刺, a2
修復了a1
的錯誤, b1
是初始嘗試使b
工作, a3
是認識到b1
要求A
完全不同, b2
修復了b1
的錯誤, a4
修復了a3
的錯誤改為a2
,而b3
應該是b1
應該做的; 然后c1
是C
的初始嘗試, b4
是b1
另一個修正, c2
是細化,依此類推。
讓我們說在c3
之后你認為它已經准備好了。 現在你運行git rebase -i origin/master
或者其他什么,將pick
行改為a1
到a4
到順序, b1
到b4
進入順序, c1
到c3
進入順序,讓rebase運行。 然后你解決任何沖突,並確保東西仍然是正確的,那么你運行另一個git rebase -i
崩潰所有四個a
版本為A
,依此類推。
當你完成所有的時候, 看起來你第一次創造了一個完美的A
(或者可能是a4
或其他一個,取決於你保留哪些提交以及你丟棄哪些提交以及是否重新設置任何時間戳) 。 其他人可能不希望或不需要看到您的中間工作 - 盡管您可以保留它, 而不是組合提交,如果這有用的話。 與此同時,您永遠不需要擁有必須重新定義的未提交的東西,因為您只是提交了部分內容。
它有助於在單行提交文本中提供這些提交名稱,以指導您以后的rebase工作:
git commit -m 'temp commit: work to enable frabulator, incomplete'
等等。
一個簡單的答案: git rebase -i --autosquash --autostash <tree-ish>
-i = interactively rebase
https://devdocs.io/git/git-rebase
這將...
<tree-ish>
交互式地重新定義
tree-ish
可以是提交哈希,也可以是分支名稱 , 標簽或任何標識符 。
您可以使用名為git-up的外部工具,它完全按照您對所有分支的說法執行操作。 這也有助於您保持干凈的歷史圖表。
我已經使用了幾年而且效果很好,特別是如果你不是git專家的話。 如果您添加自動重新定位功能,您應該知道如何從失敗的rebase中正確恢復(繼續,中止,...)
打開一個shell並運行:
sudo gem install git-up
打開全局配置文件( ~/.gitconfig
),並添加以下內容:
[git-up "fetch"]
all = true # up all branches at once, default false
prune = true # prune deleted remote branches, default false
[git-up "rebase"]
# recreate merges while rebasing, default: unset
arguments = --preserve-merges
# uncomment the following to show changed commit on rebase
# log-hook = "git --no-pager log --oneline --color --decorate $1..$2"
有關更多選項,請參閱官方文檔 。
如果一切配置都很好,只需運行:
git up
這(大致)等同於執行以下操作:
git stash
git fetch --all
[foreach branch]
git rebase --preserve-merges <branch> <remote>/<branch>
git merge --ff-only <branch>
[end foreach]
git checkout <prev_branch>
git stash pop
一個命令中的整個工作流程,包括獲取:
git pull --rebase --autostash [...]
命令別名為:
git stash && git pull --rebase && git stash pop
更新
如果您正在使用想法,使用臟工作目錄推送,它將提示對話框,選擇rebase / merge,它將執行存儲,rebase / merge並自動彈出。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.