Можно ли отправить git stash в удаленный репозиторий?

206

Можно ли в git создать тайник, перенести тайник в удаленный репозиторий, получить тайник на другом компьютере и применить тайник?

Или мои варианты:

  • Создать патч и скопировать патч на другой компьютер, или
  • Создать второстепенную ветвь и передать незавершенную работу этой ветке?
Эндрю Гримм
источник

Ответы:

68

Невозможно получить его с помощью fetch или около того, зеркальный refspec есть fetch = +refs/*:refs/*, и хотя stash refs/stashи не отправляется. Явное refs/stash:refs/stashтакже не имеет никакого эффекта!

В любом случае это только сбило бы с толку, так как это не принесло бы все тайники, только самые последние; список тайников - это рефлог реф refs/stashes.

u0b34a0f6ae
источник
4
Вы можете получить последний тайник из удаленного git, но не в свой тайник, а только в другую ссылку. Нечто подобное . Но вы не можете получить старые тайники, потому что они хранятся в журнале, который невозможно извлечь. См stackoverflow.com/questions/2248680/...git fetch some-remote +refs/stash:refs/remotes/some-remote/stashgit stash apply some-remote/stash
sj26
75

Примечание: я только что переписал этот ответ с 24-часовым гитфу под моим поясом :) В моей истории с оболочкой весь шебанг теперь состоит из трех строк. Тем не менее, я не осуждаю их для вашего удобства.

Таким образом, я надеюсь, что вы сможете увидеть, как я это делал, вместо того, чтобы просто слепо копировать / вставлять вещи.


Вот шаг за шагом.

Предположим, что источник в ~ / OLDREPO содержит тайники. Создайте тестовый клон, не содержащий тайников:

cd ~/OLDREPO
git clone . /tmp/TEST

Вставьте все тайники как временные ветви:

git send-pack /tmp/TEST $(for sha in $(git rev-list -g stash); \
    do echo $sha:refs/heads/stash_$sha; done)

Цикл на приемном конце, чтобы преобразовать обратно в тайники:

cd /tmp/TEST/
for a in $(git rev-list --no-walk --glob='refs/heads/stash_*'); 
do 
    git checkout $a && 
    git reset HEAD^ && 
    git stash save "$(git log --format='%s' -1 HEAD@{1})"
done

Очистите ваши временные ветви, если хотите

git branch -D $(git branch|cut -c3-|grep ^stash_)

Сделайте список git stash, и вы получите что-то вроде этого:

stash@{0}: On (no branch): On testing: openmp import
stash@{1}: On (no branch): On testing: zfsrc
stash@{2}: On (no branch): WIP on sehe: 7006283 fixed wrong path to binary in debianized init script (reported as part of issue
stash@{3}: On (no branch): WIP on debian-collab: c5c8037 zfs_pool_alert should be installed by default
stash@{4}: On (no branch): WIP on xattrs: 3972694 removed braindead leftover -O0 flag
stash@{5}: On (no branch): WIP on testing: 3972694 removed braindead leftover -O0 flag
stash@{6}: On (no branch): WIP on testing: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{7}: On (no branch): WIP on xattrs: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{8}: On (no branch): WIP on testing: 28716d4 fixed implicit declaration of stat64
stash@{9}: On (no branch): WIP on emmanuel: bee6660 avoid unrelated changes

На исходном репозитории тоже самое выглядело как

stash@{0}: WIP on emmanuel: bee6660 avoid unrelated changes
stash@{1}: WIP on testing: 28716d4 fixed implicit declaration of stat64
stash@{2}: WIP on xattrs: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{3}: WIP on testing: db9f77e fuse_unmount_all could be starved for the mtx lock
stash@{4}: WIP on testing: 3972694 removed braindead leftover -O0 flag
stash@{5}: WIP on xattrs: 3972694 removed braindead leftover -O0 flag
stash@{6}: WIP on debian-collab: c5c8037 zfs_pool_alert should be installed by default
stash@{7}: WIP on sehe: 7006283 fixed wrong path to binary in debianized init script (reported as part of issue #57)
stash@{8}: On testing: zfsrc
stash@{9}: On testing: openmp import
sehe
источник
1
Я многому учусь за короткое время, и я чувствую, что мне, вероятно, следует просто использовать много команд в моем более раннем подходе, что я попытаюсь сделать позже.
Сехе
9
Это хорошо сработало для меня, за исключением того, что мне нужно было git add .до git stash save ...шага, так как git stashотказывается хранить новые файлы, если они не были подготовлены. Кроме того, передача результатов git rev-list ...сквозной передачи tacменяет порядок расположения тайников, чтобы они вышли в том же порядке.
Алан Крюгер
1
@ sehe Отличный сценарий !! Два предложения: 1) - поменять окончательный реф-список, чтобы в целевом репо тайники были в том же порядке, что и в оригинале. 2) Завершите последний forцикл с помощью git branch -D stash_$a(очистите, когда создаются тайники), чтобы, если что-то пошло не так, и мы повторили попытку, мы не обрабатывали коммиты, уже успешно спрятанные.
Кит Робертсон
1
Большое вам спасибо за то, что нашли время объяснить, что вы сделали, вместо того, чтобы «просто опубликовать решение».
Марьян Венема
1
Решение может быть улучшена дополнительно: Если заменить git stash save "$(git log --format='%s' -1 HEAD@{1})"с git update-ref --create-reflog -m "$(git show -s --format=%B $rev)" refs/stash $revвами получить оригинальное копить сообщение ( update-refэто то , что git stash saveза кадром).
Себастьян Шредер
31

Я немного опаздываю на вечеринку, но я считаю, что нашел что-то, что работает для меня в этом отношении, и это может быть для вас тоже, если ваши обстоятельства такие же или похожие.

Я работаю над функцией в своей собственной ветке. Ветвь не объединяется с мастером и не выдвигается до тех пор, пока она не будет завершена или пока я не совершу коммиты, которые мне удобно показывать публике. Итак, что я делаю, когда хочу перенести не поэтапные изменения на другой компьютер:

  • Сделайте коммит с сообщением коммита типа " [non-commit] FOR TRANSFER ONLY", содержащим контент, который вы хотите передать.
  • Войдите на другой компьютер.
  • Затем сделайте:

    git pull ssh+git://<username>@<domain>/path/to/project/ rb:lb

    URL может отличаться для вас, если вы обращаетесь к своему хранилищу другим способом. Это вытянет изменения из этого URL из удаленной ветви "rb" в локальную ветку "lb". Обратите внимание, что у меня есть сервер ssh, работающий на моем собственном компьютере, и я могу получить доступ к хранилищу таким образом.

  • git reset HEAD^(подразумевает --mixed)

    Это сбрасывает HEAD, чтобы он указывал на состояние перед фиксацией «[non-commit]».

Из git-reset (1): " --mixed: Сбрасывает индекс, но не рабочее дерево (т. Е. Измененные файлы сохраняются, но не помечаются для фиксации) [...]"

Таким образом, в конце вы внесете свои изменения в файлы, но в мастере не будет зафиксировано никаких коммитов и нет необходимости в тайнике.

Это, однако, потребует от вас git reset --hard HEAD^в хранилище, в котором вы сделали «[non-commit]», так как этот коммит является мусором.

Виктор Заманян
источник
Это намного грязнее, чем просто создать новую функциональную ветвь и потом удалить ее ...
Taegost
@Taegost зависит от вашего окружения, я думаю. Может быть, что-то вроде CI / CD, не позволяющее просто толкать ветки вверх по течению. Но да, в зависимости от того, что вы предпочитаете, вы можете просто создать ветку, чтобы выполнить то же самое.
Виктор Заманян
22

Уже немного поздно, но этот ответ может кому-то помочь. Я хотел знать это, потому что я хотел иметь возможность выдвигать текущую функцию / ошибку / что угодно и работать из той же точки на другом компьютере.

Для меня работает фиксация моего текущего кода (в ветке, над которой я работаю в одиночку). Когда я доберусь до моего другого компьютера, сделайте тягу, а затем отмените коммит с помощью:

git reset --soft HEAD^

Продолжайте работать, как вы, со всеми вашими изменениями, незавершенными и неподготовленными.

Надеюсь, поможет.

Сэр роберт
источник
Когда я пытаюсь это сделать, Origin все еще поддерживает коммит, который не был передан. Близко, но без сигары для меня.
Резвитс
@rezwits Да, пульт хранит его, но достаточно просто удалить временную ветку из источника.
Сэр Роберт
на самом деле это то, что я делал!
Резвитс
19

Кажется, есть очень хороший трюк, чтобы решить эту проблему. Вы можете использовать git diff > file.diff(и зафиксировать файл), а затем восстановить изменения, используя git apply file.diff(из любого места) для достижения того же результата.

Это было объяснено и здесь .

Даниэль Дубовски
источник
5
если у вас есть неотслеживаемые файлы: 1. git add. 2. git diff HEAD> file.diff
trickpatty
Отправка сообщения diff для себя не допускает никаких коммитов / следов на репо! (например: Note-to-self через настольное приложение Signal) или по электронной почте.
Джон Ми
9

Я бы выбрал второй подход, хотя понятия не имею, почему вы не можете зафиксировать его в основной / избранной ветке. Можно также собирать вишню.

Eimantas
источник
27
Нет никакой технической причины не делать коммит на master / featured, просто хочу сказать: «Это не настоящий коммит, это просто сохранение моей работы, чтобы я мог получить ее на другой машине».
Эндрю Гримм
4

AFAIK вся идея тайника в том, чтобы спрятать что-то не столь важное под местным ковром. Никто не должен знать о вашем любимом дерьме ;-) Единственное «но» - это: а если я буду работать на паре рабочих станций? Тогда scpэто намного лучше.

argent_smith
источник
9
Что-то смешное должно быть комментарием. ;-)
Эндрю Гримм
2
Всего git-ssh-newbie здесь, но можете ли вы тогда использовать scp с github?
Коен
Нет, интерфейс git-ssh github запрограммирован так, что у вас никогда не будет оболочки / консоли ssh. Он может запускать только серверный процесс git.
argent_smith
1
Таким образом, scp на самом деле не вариант для этого сценария, если ваша основная ветка находится на github? Любые другие предложения для передачи тайника в этом случае?
Коен
1
Я пытался подчеркнуть, что передача тайника вообще невозможна, AFAIK.
argent_smith
2

В настоящее время принятый ответ является технически правильным, вы не можете напрямую сказать Git, чтобы он перенес все ваши тайники на удаленный компьютер, а затем перенес все в свои локальные тайники на другом компьютере.

И хотя в настоящее время ответ с наивысшим рейтингом должен работать, мне не понравилось, что он создает кучу временных веток и требует ручного извлечения фиксации stash и сохранения его как stash, что может привести к таким проблемам, как этот комментарий. упоминается , и приводит к дублированию On (no branch): On testing:. Конечно, должен быть лучший способ!

Таким образом, хотя вы не можете напрямую выдвигать тайники, тайник - это просто коммит (на самом деле это два коммита), и на git pushстранице man вы можете нажать коммиты:

<src>Часто название отрасли вы хотели бы, чтобы толкать, но он может быть любым произвольным «SHA-1 выражение» ...

Я предпочел подтянуть крошку, чтобы refs/stashes/*не загромождать мой пульт дополнительными ветвями. Так что я могу сделать это с:

git push origin stash@{0}:refs/stashes/$(git rev-parse --short stash@{0})

(Команда rev-parseполучает короткий хэш тайника, который будет уникальным для репо.)

Затем мне нужно получить тайник с другого компьютера. Git по умолчанию выбирает только ветки, так что мне нужно специально выбирать тайники:

git fetch origin refs/stashes/*:refs/stashes/*

Теперь, чтобы преобразовать тайник коммит обратно в реальный тайник. Как уже упоминалось, хотя я мог просто проверить фиксацию, сброс и сохранение тайника, как обычно, мне не нравится, что для этого требуются дополнительные шаги или что он может не поддерживать состояние индекса для тайника. Я искал в Интернете способ сделать это автоматически, но мой поиск не помог мне. Наконец, я просмотрел страницу руководства для git stash, где я нашел это:

create
Создает stash (который является обычным объектом коммита) и возвращает его имя объекта, не сохраняя его где-либо в пространстве имен ref. Это должно быть полезно для сценариев. Вероятно, это не та команда, которую вы хотите использовать; см. «сохранить» выше.

store
Сохраняет данный stash, созданный с помощью git stash create (который представляет собой висячий коммит слияния), в stash ref, обновляя stash reflog. Это должно быть полезно для сценариев. Вероятно, это не та команда, которую вы хотите использовать; см. «сохранить» выше.

Поскольку у меня уже есть коммит, storeзвучит как то, что я хочу. Так что я могу сделать:

git stash store --message "$(git show --no-patch --format=format:%s <SHA>)" <SHA>

Замена <SHA>на тайник, который был только что извлечен.

(Команда git showполучает сообщение о коммите из коммита stash, которое используется в качестве сообщения для журнала тайников.)

Тайник теперь отображается как обычно в моем локальном репо:

$ git stash list
stash@{0}: On master: temp
...

Для очистки пульта можно удалить тайники с пульта следующим образом:

git push origin :refs/stashes/<SHA>

Этот метод также имеет то преимущество, что он является идемпотентом: если вы pushснова запустите команду, она сообщит Everything up-to-date. fetchКоманда также может быть безопасно запустить повторно. Несмотря на то, что stash storeон пропустит сохранение тайника, если он совпадает с последним тайником, он не предотвратит дублирование старых тайников. Это можно обойти, хотя, как я делаю в моем git-rstashсценарии, см. Ниже.


Для завершения, вы также можете легко нажать на все тайники (с ):

for i in $(seq 0 $(expr $(git rev-list --walk-reflogs --count stash) - 1))
do
  git push origin stash@{$i}:refs/stashes/$(git rev-parse --short stash@{$i})
done

или импортировать все извлеченные тайники:

for stash in $(ls .git/refs/stashes)
do
  git stash store --message "$(git show --no-patch --format=format:%s $stash)" $stash
done

Я создал сценарий, который может быть вызван как подкоманда (например git rstash push 0), поэтому мне не нужно все это помнить. git-rstashможно найти здесь.

Скотт Уэлдон
источник
1

Следующее не работает с тайником, но с незафиксированными изменениями в рабочем каталоге. Он создает ветку, автоматически фиксирует все текущие изменения и отправляет на удаленный:

commit_and_push_ ( ) {
    # This will:
    #  1. checkout a new branch stash-XXX
    #  2. commit the current changes in that branch
    #  3. push the branch to the remote
    local locbr=${1:-autostash-XXX}
    git checkout -b $locbr
    git add .
    git commit -a -m "Automatically created commit"
    git push origin $locbr
    echo "Autocommitted changes in branch $locbr ..."
}

Используйте как:

commit_and_push_ my-temp-branch
commit_and_push_
blueFast
источник
0

Я просто создал бы новую ветку stash и удалял всякий раз, когда эта ветка не требуется.

git add . // Add work-in-progress job
git checkout -b stash-branch // Create and checkout to stash-branch
git commit -m 'WIP: job description' // Commit message
git push origin stash-branch // Push to remote
git pull origin stash-branch // Pull the stash-branch
git checkout master // Checkout to working branch
git rebase stash-branch // Rebase the stash-branch
git reset --soft // Equivalent to stash!!
git branch -d stash-branch // Delete when not needed from local
git push -d origin stash-branch // Delete when not needed from remote
Бхойендра Раунияр
источник
-9

Просто используйте Dropbox, как этот парень. Таким образом, вам не нужно беспокоиться о загрузке тайников, поскольку весь ваш код будет сохранен.

http://blog.sapegin.me/all/github-vs-dropbox

NYC Tech Engineer
источник
2
Прежде чем у кого-нибудь возникнет реакция коленного рефлекса, я не говорю о том, чтобы использовать Dropbox вместо Github, но чтобы хранить код, который не готов к фиксации, в Dropbox, который все еще находился бы под контролем версий локально.
Технический инженер Нью-Йорка
4
копирование всего проекта в удаленное облако займет слишком много времени.
Став Альфи