автоматическое сохранение/поп-изменений в git rebase?
мой рабочий процесс git использует 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 = failed rebase, 2=stash, 3=rebase, 4=stash pop. ничего, но 3-это просто бессмысленная работа.
Итак, вопрос: каков наиболее рекомендуемый способ автоматизировать? псевдоним для запуска git stash/rebase / pop каждый раз? некоторая конфигурация git, которая заставляет rebase скрывать или рассматривать ее как другую фиксацию, чтобы повторно применить впоследствии? что-то еще?
5 ответов
Edit: начиная с Git версии 1.8.4, но с важной боковой ошибкой, исправленной в Git версии 2.0.1,git rebase
теперь --autostash
. Вы можете настроить git rebase
использовать --autostash
по умолчанию, а также, с git config --global rebase.autoStash true
. Обратите внимание на следующее предложение из документация:
однако, используйте с осторожностью: окончательный тайник применение после успешного rebase может привести к нетривиальным рознь.
(я все же предпочитаю просто сделайте обязательства.)
TL; DR ответ: просто сделайте фиксацию (затем отмените ее позже)
это может помочь вам понять, что 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
. The i-w
висит совершения C
это ваш тайник, который не находится на ветке, но все еще является два-commit "git stash bag" и фактически прикреплен к вашей последней фиксации (C
). The @
commits (может быть только один) - это новые восходящие коммиты.
(если вы сделали нет commits, ваш тайник висит от commit *
, и ваша текущая ветвь указывает на commit *
, так что git rebase
не имеет никакой работы, кроме перемещения текущего указателя ветви вперед. В этом случае все будет по-прежнему, но я ... предположим, есть некоторые коммиты.)
запустить git rebase upstream/master
. Это копирует ваши коммиты в новые коммиты с новыми идентификаторами и новыми родительскими идентификаторами, чтобы они сидели поверх последнего @
. Тайник-мешок не двигается, поэтому результат выглядит так:
... - 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
цепь, и значит нам не нужно i-w
бит либо, что позволяет нам перерисовать это как гораздо проще:
... - @ <-- upstream/master
\
A'-B'-C' <-- HEAD=master plus work tree changes
теперь давайте нарисуем, что произойдет, если вместо git stash save
, ты git commit -a
(или git add
и git commit
без -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
единственная проблема заключается в том, что у вас есть этот нежелательный дополнительный commit D
(ну, D'
now), вместо незафиксированных изменений рабочего дерева. Но это тривиально отменено с git reset
сделать шаг назад один коммит. Мы можем использовать --mixed
reset-по умолчанию-чтобы получить индекс (промежуточная область) повторно установить тоже, чтобы "un-add" все файлы, или если вы хотите, чтобы они остались git add
- ed, a --soft
сброс. (Ни один из них не влияет на результирующий график фиксации, только состояние индекса быть другой.)
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: make много временной фиксации, чтобы сохранить вашу работу, как вы идете. Вы всегда можете отложить их позже. То есть вместо этого:
... - * - A - B - C <-- mybranch
здесь A
, B
и C
идеальны и окончательные коммиты поверх commit *
(от кого-то другого или ранее опубликованного материала), сделайте это:
... - * - a1 - a2 - b1 - a3 - b2 - a4 - b3 - c1 - b4 - c2 - c3
здесь a1
это начальный удар по A
, a2
Исправлена ошибка в a1
, b1
это первая попытка make b
работы, a3
от осознания этого b1
требует A
быть другим в конце концов,b2
Исправлена ошибка в b1
, a4
Исправлена ошибка в a3
'ы изменение a2
и b3
это b1
надо было сделать; потом c1
является первоначальной попыткой C
, b4
другое исправить b1
, c2
это уточнение, и так далее.
скажем, что после c3
вы думаете, что он в основном готов. Теперь беги!--85--> или что, перетасуйте 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
это будет...
- Auto stash ваши изменения
- интерактивно перебазировать из
<tree-ish>
- автоматическое положение ваших squashes и fixups
- Auto pop stash в рабочем каталоге после rebase
tree-ish
может быть совершить хэш или филиала или tag или идентификатор.
вы можете использовать внешние инструменты под названием git-up, который делает именно то, что вы говорите для всех отраслей. Это также поможет вам сохранить чистый график истории.
я использовал в течение нескольких лет, и он работает довольно хорошо, особенно если вы не эксперт в ГИТ. Если вы добавляете функцию автоматического перебазирования, вы должны знать, как правильно восстанавливаться после сбоя ребазирования (продолжить, прервать,...)
установить
откройте оболочку и беги:
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 .."
посмотреть официальная документация дополнительные параметры.
ссылка
если все настроено хорошо, просто запустите:
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
ответ tjsingleton blogg
сделайте псевдоним команды:
в Git тайник && ГИТ тянуть --перебазировать && Git в заначке поп
обновление
Если вы используете idea, нажимая с грязным рабочим реж, он предложит диалог, выберите rebase / merge, он будет делать stash, rebase / merge и pop автоматически.