Có thể đẩy một git stash vào một kho lưu trữ từ xa?


204

Trong git, có thể tạo stash, đẩy stash vào kho lưu trữ từ xa, truy xuất stash trên một máy tính khác và áp dụng stash không?

Hoặc là những lựa chọn của tôi:

  • Tạo một bản vá và sao chép bản vá vào máy tính khác, hoặc
  • Tạo một nhánh nhỏ và cam kết công việc không hoàn thành cho nhánh đó?

Câu trả lời:


68

Không thể có được nó thông qua tìm nạp hoặc như vậy, refspec gương là fetch = +refs/*:refs/*, và mặc dù stash là refs/stashnó không được gửi. Một tường minh refs/stash:refs/stashcũng không có tác dụng!

Dù sao thì nó cũng chỉ gây nhầm lẫn vì điều đó sẽ không lấy tất cả các stash, chỉ một cái mới nhất; danh sách các stash là reflog của ref refs/stashes.


4
Bạn có thể tìm nạp stash mới nhất từ ​​một git remote, nhưng không vào stash của bạn, chỉ vào một ref khác. Một cái gì đó giống như git fetch some-remote +refs/stash:refs/remotes/some-remote/stashsự git stash apply some-remote/stash. Nhưng bạn không thể nhận được các bản cũ hơn vì chúng được lưu trữ trong bản reflog không thể tải được. Xem stackoverflow.com/questions/2248680/ từ
sj26

75

Lưu ý: Tôi vừa viết lại câu trả lời này với 24 giờ git-fu trong vành đai của mình :) Trong lịch sử vỏ của tôi, toàn bộ shebang bây giờ là ba lớp lót. Tuy nhiên, tôi đã giải phóng chúng để thuận tiện cho bạn.

Bằng cách này, tôi hy vọng bạn sẽ có thể thấy tôi đã làm mọi thứ như thế nào, thay vì chỉ cần sao chép / dán một cách mù quáng.


Đây là từng bước.

Giả sử là nguồn trong ~ / OLDREPO chứa stash. Tạo một bản sao TEST không chứa stash:

cd ~/OLDREPO
git clone . /tmp/TEST

Đẩy tất cả các stash như các nhánh tạm thời:

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

Lặp lại ở đầu nhận để chuyển đổi thành stash:

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

Dọn dẹp các chi nhánh tạm thời của bạn nếu bạn sẽ

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

Thực hiện một danh sách git stash và bạn sẽ làm như thế này:

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

Trên kho lưu trữ ban đầu, trông giống như

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

1
Tôi đang học được rất nhiều trong thời gian ngắn, và tôi cảm thấy có lẽ tôi chỉ đơn giản là nên sử dụng nhiều lệnh trong cách tiếp cận trước đây của mình, điều mà tôi sẽ cố gắng thực hiện sau.
sehe

9
Điều này hoạt động tốt với tôi ngoại trừ việc tôi cần một bước git add .trước khi git stash save ...từ git stashchối bỏ tập tin mới trừ khi chúng được dàn dựng. Ngoài ra, đường ống kết quả git rev-list ...thông qua tacđảo ngược thứ tự của các stash để chúng đi ra theo thứ tự tương tự.
Alan Krueger

1
@sehe Kịch bản tuyệt vời !! Hai gợi ý: 1) - đảo ngược danh sách giới thiệu cuối cùng để các stash theo cùng thứ tự trong repo đích như trong bản gốc. 2) Kết thúc forvòng lặp cuối cùng với git branch -D stash_$a(dọn sạch khi stash được tạo) để nếu xảy ra sự cố và chúng tôi thử lại, chúng tôi không tái xử lý các cam kết đã được xử lý thành công.
Keith Robertson

1
Cảm ơn bạn rất nhiều vì đã dành thời gian để giải thích những gì bạn đã làm thay vì "chỉ đăng giải pháp".
Marjan Venema

1
Giải pháp có thể được cải thiện hơn nữa: Nếu bạn thay thế git stash save "$(git log --format='%s' -1 HEAD@{1})"bằng git update-ref --create-reflog -m "$(git show -s --format=%B $rev)" refs/stash $revbạn nhận được thông báo gốc ban đầu ( update-reflà những gì git stash saveđằng sau hậu trường).
Sebastian Schrader

31

Tôi đến bữa tiệc muộn một chút, nhưng tôi tin rằng tôi đã tìm thấy thứ gì đó phù hợp với tôi về điều này và nó cũng có thể cho bạn nếu hoàn cảnh của bạn giống hoặc tương tự.

Tôi đang làm việc trên một tính năng trong chi nhánh riêng của mình. Chi nhánh không được hợp nhất thành chủ và được đẩy cho đến khi hoàn thành hoặc tôi đã cam kết rằng tôi cảm thấy thoải mái khi hiển thị với công chúng. Vì vậy, những gì tôi làm khi tôi muốn chuyển các thay đổi không theo giai đoạn sang một máy tính khác là:

  • Tạo một cam kết, với một thông điệp cam kết như " [non-commit] FOR TRANSFER ONLY", có nội dung bạn muốn chuyển.
  • Đăng nhập vào máy tính khác.
  • Sau đó làm:

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

    URL có thể khác với bạn nếu bạn truy cập kho lưu trữ của mình theo một cách khác. Điều này sẽ kéo các thay đổi từ URL đó từ nhánh "rb" từ xa sang nhánh cục bộ "lb". Lưu ý rằng tôi có một máy chủ ssh chạy trên máy tính của riêng tôi và có thể truy cập kho lưu trữ theo cách đó.

  • git reset HEAD^(ngụ ý --mixed)

    Điều này đặt lại ĐẦU để trỏ đến trạng thái trước khi xác nhận "[không cam kết]".

Từ git-reset (1): " --mixed: Đặt lại chỉ mục nhưng không phải cây làm việc (nghĩa là các tệp đã thay đổi được giữ nguyên nhưng không được đánh dấu cho cam kết) [...]"

Vì vậy, cuối cùng bạn sẽ có các thay đổi của mình đối với các tệp, nhưng không có cam kết nào được thực hiện để làm chủ và không cần stash.

Tuy nhiên, điều này sẽ yêu cầu bạn git reset --hard HEAD^vào kho lưu trữ mà bạn đã thực hiện "[không cam kết]", vì cam kết đó là rác.


Điều này bẩn hơn rất nhiều sau đó chỉ cần tạo một nhánh tính năng mới sau đó xóa nó sau đó ....
Taegost

@Taegost phụ thuộc vào môi trường của bạn, tôi đoán. Có thể là một số công cụ CI / CD ngăn chặn việc đẩy các nhánh ngược dòng lên xuống. Nhưng có, tùy thuộc vào những gì bạn thích, bạn có thể muốn tạo một nhánh để hoàn thành điều tương tự.
Victor Zamanian

22

Hơi muộn một chút, nhưng câu trả lời này có thể giúp được ai đó. Tôi muốn biết điều này bởi vì tôi muốn có thể đẩy một tính năng / lỗi đang diễn ra / bất cứ điều gì và làm việc từ cùng một điểm trên một máy tính khác.

Điều làm việc cho tôi là cam kết mã đang thực hiện của tôi (trong một chi nhánh mà tôi đang làm việc một mình). Khi tôi đến máy tính khác của mình, thực hiện thao tác kéo, sau đó hoàn tác cam kết với:

git reset --soft HEAD^

Tiếp tục làm việc như bạn đã làm, với tất cả các thay đổi đang diễn ra ở đó, không được cam kết và không được tổ chức.

Hy vọng nó giúp.


Khi tôi cố gắng làm điều này, Nguồn gốc vẫn duy trì Cam kết, điều đó không được cam kết. Đóng nhưng không có xì gà cho tôi.
thiệu

@rezwits Vâng, điều khiển từ xa giữ nó, nhưng thật dễ dàng để xóa chi nhánh tạm thời khỏi nguồn gốc.
Ngài Robert

như một vấn đề của thực tế đó là những gì tôi đã và đang làm!
thiệu

19

Dường như có một mẹo rất gọn gàng để giải quyết điều này. bạn có thể sử dụng git diff > file.diff(và cam kết tệp), sau đó khôi phục các thay đổi bằng cách sử dụng git apply file.diff(từ bất cứ đâu) để đạt được kết quả tương tự.

Điều này đã được giải thích ở đây là tốt.


5
nếu bạn có các tập tin chưa được theo dõi: 1. git add. 2. git diff HEAD> file.diff
trickpatty

Nhắn tin cho diff cho chính bạn cho phép không có cam kết / dấu chân trên repo cả! (ví dụ: Tự ghi chú thông qua ứng dụng máy tính để bàn Tín hiệu) hoặc email.
John Mee

9

Tôi sẽ tiếp cận với cách tiếp cận thứ hai mặc dù không biết tại sao bạn không thể cam kết nó thành nhánh chính / đặc trưng. Có thể làm anh đào quá.


27
Không có lý do kỹ thuật nào để không cam kết làm chủ / nổi bật, chỉ là tôi muốn nói "Đây không phải là một cam kết thực sự, nó chỉ tiết kiệm công việc của tôi để tôi có thể lấy nó trên một máy khác".
Andrew Grimm

4

AFAIK toàn bộ ý tưởng của stash là để che giấu một thứ không quan trọng dưới tấm thảm địa phương . Không ai nên biết về crap yêu thích của bạn ;-) "Nhưng" duy nhất là: Nhưng nếu tôi phát triển trên một vài máy trạm? Sau đó scplà cách tốt hơn.


9
Một cái gì đó buồn cười nên là một bình luận. ;-)
Andrew Grimm

2
Tổng số git-ssh-newbie ở đây nhưng bạn có thể sử dụng scp với github không?
Koen

Không, giao diện git-ssh của github được lập trình để bạn không bao giờ có ssh shell / console. Nó chỉ có thể chạy quá trình git phía máy chủ.
argent_smith

1
Vì vậy, scp không thực sự là một lựa chọn cho kịch bản này nếu nhánh chính của bạn nằm trên github? Bất kỳ đề nghị khác để chuyển một stash trong trường hợp đó?
Koen

1
Tôi đã cố gắng nhấn mạnh rằng chuyển stash hoàn toàn không thể, AFAIK.
argent_smith 17/03/2016

2

Các câu trả lời chấp nhận hiện nay là chính xác về mặt kỹ thuật, bạn không thể trực tiếp nói với Git để đẩy tất cả ẩn nấp của bạn đến một vùng xa, và sau đó kéo tất cả mọi thứ vào ẩn nấp địa phương của bạn trên máy tính khác.

Và trong khi câu trả lời được nâng cấp hiện tại nên hoạt động, tôi không thích rằng nó tạo ra một loạt các nhánh tạm thời và nó yêu cầu kiểm tra thủ công stash và lưu nó dưới dạng stash, điều này có thể dẫn đến các vấn đề như nhận xét này đã đề cập , và dẫn đến một bản sao On (no branch): On testing:. Chắc chắn phải có một cách tốt hơn!

Vì vậy, trong khi bạn không thể trực tiếp đẩy stash, thì stash chỉ là một cam kết (thực tế là hai lần xác nhận) và trên mỗi git pushtrang man bạn có thể đẩy các cam kết:

Đây <src>thường là tên của chi nhánh bạn muốn đẩy, nhưng nó có thể là bất kỳ "biểu thức SHA-1" tùy ý nào ...

Tôi đã chọn đẩy các stash để refs/stashes/*không làm hỏng điều khiển từ xa của mình với các nhánh phụ. Vì vậy, tôi có thể làm điều đó với:

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

( rev-parseLệnh nhận được hàm băm ngắn của stash, sẽ là duy nhất cho repo.)

Tiếp theo, tôi cần lấy stash từ máy tính khác. Git chỉ tìm nạp các nhánh theo mặc định, vì vậy tôi cần tìm nạp các stash cụ thể:

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

Bây giờ để chuyển đổi stash commit trở lại thành một stash thực tế. Như đã đề cập, trong khi tôi chỉ có thể kiểm tra stash commit, đặt lại và stash như bình thường, tôi không muốn nó đòi hỏi các bước bổ sung hoặc nó có thể không duy trì trạng thái chỉ mục cho stash. Tôi đã tìm kiếm trực tuyến một cách để làm điều đó tự động, nhưng tìm kiếm của tôi đã làm tôi thất bại. Cuối cùng tôi xem qua trang người đàn ông git stash, nơi tôi tìm thấy cái này:

tạo
Tạo một stash (là một đối tượng cam kết thông thường) và trả về tên đối tượng của nó, mà không lưu trữ nó ở bất cứ đâu trong không gian tên ref. Điều này được dự định sẽ hữu ích cho các kịch bản. Nó có thể không phải là lệnh bạn muốn sử dụng; xem "lưu" ở trên.

cửa hàng
Lưu trữ một stash nhất định được tạo thông qua git stash tạo (đó là một cam kết hợp nhất lơ lửng) trong stash ref, cập nhật stash reflog. Điều này được dự định sẽ hữu ích cho các kịch bản. Nó có thể không phải là lệnh bạn muốn sử dụng; xem "lưu" ở trên.

Vì tôi đã có cam kết, storeâm thanh như những gì tôi muốn. Vì vậy, tôi có thể làm:

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

Thay thế <SHA> bằng stash vừa được tải.

(Các git show Lệnh nhận thông báo cam kết từ cam kết stash, để sử dụng làm thông báo cho nhật ký stash.)

Stash bây giờ xuất hiện như bình thường trong repo địa phương của tôi:

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

Để dọn dẹp điều khiển từ xa, các stash có thể bị xóa khỏi điều khiển từ xa như vậy:

git push origin :refs/stashes/<SHA>

Phương pháp này cũng có lợi ích là không cần thiết: nếu bạn chạy lại pushlệnh, nó sẽ báo cáo Everything up-to-date. Các fetchlệnh cũng có thể chạy một cách an toàn liên tục. Mặc dù stash storesẽ bỏ qua việc lưu trữ stash nếu nó giống với stash gần đây nhất, nhưng nó không ngăn chặn các bản sao cũ của stash. Điều này có thể được giải quyết xung quanh, như tôi làm trong git-rstashkịch bản của mình , xem bên dưới.


Để hoàn thành, bạn cũng có thể dễ dàng đẩy tất cả các stash (với ):

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

hoặc nhập tất cả các bản tải xuống:

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

Tôi đã tạo ra một tập lệnh có thể được gọi là một tiểu ban (ví dụ git rstash push 0) vì vậy tôi không phải nhớ tất cả điều này. git-rstashcó thể được tìm thấy ở đây.


1

Những điều sau đây không hoạt động với stash, nhưng với những thay đổi không được cam kết trong thư mục làm việc. Nó tạo ra một nhánh, tự động kiểm tra tất cả các thay đổi hiện tại và đẩy ra điều khiển từ xa:

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 ..."
}

Sử dụng như:

commit_and_push_ my-temp-branch
commit_and_push_

0

Tôi chỉ đơn giản là tạo một nhánh stash mới và loại bỏ bất cứ khi nào nhánh đó không được yêu cầu.

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

Chỉ cần sử dụng Dropbox như anh chàng này đã làm. Bằng cách đó, bạn không phải lo lắng về việc đẩy stash vì tất cả mã của bạn sẽ được sao lưu.

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


2
Trước khi bất kỳ ai có phản ứng giật đầu gối, tôi không nói sẽ sử dụng Dropbox thay vì Github, nhưng để lưu trữ mã chưa sẵn sàng cho một cam kết trong Dropbox vẫn sẽ được kiểm soát phiên bản cục bộ.
Kỹ sư công nghệ NYC

4
Sẽ mất quá nhiều thời gian để sao chép tất cả dự án vào đám mây từ xa.
Stav Alfi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.