Ответ 1
Изменить: Начиная с версии Git версии 1.8.4, но с важной боковой ошибкой, зафиксированной в Git версии 2.0.1, git rebase
теперь имеет --autostash
. Вы можете настроить git rebase
на использование --autostash
по умолчанию, используя git config --global rebase.autoStash true
. Обратите внимание на следующее предложение документации:
Однако, используйте с осторожностью: последний тайник приложение после успешной перезагрузки может привести к нетривиальному конфликты.
(Я по-прежнему предпочитаю делать коммиты.)
TL; DR ответ: просто сделайте фиксацию (затем снимите ее позже)
Это может помочь вам понять, что git stash
на самом деле просто git commit
(в более сложной форме, которая сначала фиксирует индекс, а затем - дерево работы - при применении кошелька вы можете поддерживать разделение index и work-tree, или объединить их в просто изменение дерева работы).
То, что делает специальный штамп, заключается в том, что он совершает это: два или, с -u
или -a
, даже три коммиты - выполняются в необычной форме (в качестве фиксации слияния, которая на самом деле не является слиянием) и не помещается на какую-либо ветвь (вместо этого используется специальная ссылка refs/stash
, чтобы сохранить и найти их).
Поскольку они не находятся на ветке, rebase
не касается их, а в вашем рабочем процессе это git stash pop
, которое приносит изменения дерева работ в новое рабочее дерево. Однако, если вы сделаете свою собственную (нормальную) фиксацию, на ветке и переустановите и включите эту фиксацию, эта нормальная фиксация будет переустановлена вместе с любыми другими. Через минуту мы займемся одной последней проблемой; на данный момент, давайте нарисуем это, так как серия коммитов, которые (или не делают), переустанавливаются:
... 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
\
@[email protected]@ <-- upstream/master
Здесь A
, B
и C
- ваши коммиты (предположим, вы сделали 3), все на ветке master
. i-w
hanging off commit C
- ваш трюк, который не находится на ветке, но по-прежнему представляет собой двухпартийный git пакетный портфель и фактически привязан к вашей последней фиксации (C
). Записывается @
(может быть только один) - это новый восходящий поток.
(Если вы не совершили никаких коммитов, ваша сумка для зависания отменяет фиксацию *
, и ваша текущая ветка указывает на фиксацию *
, так что git rebase
не имеет работы, кроме перемещения текущего указателя ветвления вперед. В этом случае все работает одинаково, но я предполагаю, что есть некоторые коммиты.)
Теперь вы запустите git rebase upstream/master
. Это копирует ваши коммиты на новые коммиты, с новыми идентификаторами и новыми родительскими идентификаторами, чтобы они сидели поверх последнего @
. Шкатулка не перемещается, поэтому результат выглядит следующим образом:
... - o - * - A - B - C [abandoned, except for the stash]
\ |\
\ i-w <-- stash
\
@[email protected]@ <-- upstream/master
\
A'-B'-C' <-- HEAD=master
Теперь вы используете git stash pop
, который восстанавливает элемент i/w в качестве изменений дерева работы, стирая метку stash
(точнее, выталкивая ее так, чтобы [email protected]{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), чтобы создать фактическое commit D
. Вы начинаете с:
... - o-*-A-B-C-D <-- HEAD=master
\
@[email protected]@ <-- upstream/master
Теперь вы git rebase upstream/master
, который копирует A
через D
, чтобы поместить их в конец последнего @
, и у вас есть это:
... - o-*[email protected]@[email protected] <-- upstream/master
\
A'-B'-C'-D' <-- HEAD=master
Единственная проблема заключается в том, что у вас есть этот лишний дополнительный commit D
(ну, теперь D'
), а не без изменений. Но это тривиально отменено с помощью git reset
, чтобы отступить на один фиксатор. Мы можем использовать --mixed
reset - по умолчанию - для того, чтобы изменить индекс (область постановки), чтобы "un-add" всех файлов или если вы хотите, чтобы они остались git add
- ed, a --soft
reset. (Не влияет на итоговый граф фиксации, только состояние индекса отличается.)
git reset --mixed HEAD^ # or leave out `--mixed` since it the default
Вот что это выглядит:
... - o-*[email protected]@[email protected] <-- upstream/master
\
A'-B'-C' <-- HEAD=master
\
D' [abandoned]
Возможно, вы считаете, что это неэффективно, но когда вы используете git stash
, вы на самом деле делаете не менее двух коммитов, которые затем вы покидаете позже, когда вы git stash pop
их. Реальная разница заключается в том, что, совершая временные комманды не для публикации, вы автоматически их переустанавливаете.
Не бойтесь временных коммитов
Существует общее правило с git: сделать много временных коммитов, чтобы сохранить вашу работу по ходу дела. Вы всегда можете переустановить их позже. То есть вместо этого:
... - * - A - B - C <-- mybranch
где A
, B
и C
являются совершенными и окончательными совершаются на вершине commit *
(от кого-то другого или ранее опубликованного материала), сделайте следующее:
... - * - a1 - a2 - b1 - a3 - b2 - a4 - b3 - c1 - b4 - c2 - c3
где a1
- начальный stab at 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
в порядок, и пусть перезапуск. Затем вы исправляете любые конфликты и убедитесь, что все еще правильно, затем вы запускаете еще один git rebase -i
, чтобы свернуть все четыре версии A
в A
и т.д.
Когда все закончится, похоже, что вы создали идеальный A
в первый раз (или, может быть, с a4
или какой-либо другой, в зависимости от того, что вы обязуетесь и какие из них вы отбрасываете, устанавливать любые отметки времени в вещах). Другие люди могут не хотеть или не видеть вашу промежуточную работу, хотя вы можете сохранить ее, а не комбинировать коммиты, если это полезно. Между тем вам никогда не нужно иметь непредусмотренные вещи, которые вам нужно переустанавливать, потому что вы просто совершаете частичные вещи.
Это помогает дать эти имена коммитов в текстовом тексте с одной строкой, который будет вести вашу последующую работу по перестановке:
git commit -m 'temp commit: work to enable frabulator, incomplete'
и т.д.