Làm thế nào để bạn chỉ đẩy một số cam kết git địa phương của bạn?


160

Giả sử tôi có 5 cam kết địa phương. Tôi muốn chỉ đẩy 2 người trong số họ đến một repo tập trung (sử dụng quy trình làm việc theo kiểu SVN). Làm thế nào để tôi làm điều này?

Điều này đã không làm việc:

git checkout HEAD~3  #set head to three commits ago
git push #attempt push from that head

Điều đó kết thúc đẩy tất cả 5 cam kết địa phương.

Tôi cho rằng tôi có thể thực hiện thiết lập lại git để thực sự hoàn tác các cam kết của mình, tiếp theo là git stash và sau đó git đẩy - nhưng tôi đã có các thông báo cam kết được viết và các tệp được sắp xếp và tôi không muốn làm lại chúng.

Cảm giác của tôi là một số cờ được truyền để đẩy hoặc đặt lại sẽ hoạt động.

Nếu nó giúp, đây là cấu hình git của tôi

[ramanujan:~/myrepo/.git]$cat config 
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = ssh://server/git/myrepo.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master

Câu trả lời:


192

Giả sử các cam kết của bạn nằm trên nhánh chính và bạn muốn đẩy chúng đến nhánh chính từ xa:

$ git push origin master~3:master

Nếu bạn đang sử dụng git-svn:

$ git svn dcommit master~3

Trong trường hợp của git-svn, bạn cũng có thể sử dụng HEAD ~ 3, vì nó đang mong đợi một cam kết. Trong trường hợp git thẳng, bạn cần sử dụng tên chi nhánh vì HEAD không được đánh giá đúng trong refspec.

Bạn cũng có thể có một cách tiếp cận lâu hơn về:

$ git checkout -b tocommit HEAD~3
$ git push origin tocommit:master

Nếu bạn đang tạo thói quen cho loại công việc này, bạn nên xem xét thực hiện công việc của mình trong một nhánh riêng biệt. Sau đó, bạn có thể làm một cái gì đó như:

$ git checkout master
$ git merge working~3
$ git push origin master:master

Lưu ý rằng phần "origin master: master" có thể là tùy chọn cho thiết lập của bạn.


14
Lưu ý: bạn không phải sử dụng master~3. Bất kỳ tài liệu tham khảo để mong muốn "lên đến" cam kết là giá trị ngang nhau, chẳng hạn như HEAD~3hay HEAD~~~, hoặc SHA cụ thể, hoặc một thẻ mà nhãn cam kết.
Kaz

2
Đồ tốt. Một cảnh báo mặc dù: những ví dụ này đẩy đến nguồn gốc. Nếu bạn đang sao chép và dán giải pháp này, cuối cùng bạn có thể vô tình cập nhật nhánh chính. (Tất nhiên, bạn phải luôn cẩn thận và kiểm tra kỹ lệnh của mình trước khi ban hành git push...)
sáng lập

Có vẻ như điều này đẩy cam kết, nhưng không thêm chi nhánh từ xa.
Nateowami

@Nateowami cho rằng bạn sẽ cần chỉ định một cái gì đó ngoài masterphần từ xa của refspec, nhưgit push origin tocommit:newbramch
Ryan Graham

Tôi hiểu rồi. Tên chi nhánh đã tồn tại ở địa phương; Tôi cho rằng nó đã không như thế. Điều khiển từ xa không có tên chi nhánh mặc dù.
Nateowami

16

Những gì tôi làm là làm việc trên một chi nhánh địa phương gọi là "công việc". Chi nhánh này chứa tất cả các cam kết tạm thời (như cách giải quyết hoặc tùy chọn xây dựng riêng hoặc bất cứ điều gì) mà tôi không có ý định đẩy lên kho lưu trữ ngược dòng. Tôi làm việc đi vào chi nhánh, sau đó khi tôi muốn cam kết tôi chuyển sang các ngành thạc sĩ, anh đào-chọn các cam kết phù hợp mà tôi làm muốn cam kết, sau đó đẩy tổng thể.

Sau khi kéo các thay đổi từ thượng nguồn vào nhánh chủ của tôi, tôi git checkout workgit rebase master. Điều đó viết lại tất cả những thay đổi cục bộ của tôi là ở cuối lịch sử.

Tôi thực sự đang sử dụng git svnvới quy trình công việc này, vì vậy hoạt động "đẩy" của tôi liên quan git svn dcommit. Tôi cũng sử dụng tigmột trình xem kho lưu trữ gui chế độ văn bản đẹp, để chọn các cam kết thích hợp để làm chủ.


với git svn dcommit, bạn có thể chỉ định một cam kết với dcommit, do đó, hiệu quả mong muốn là khá nhỏ với git-svn.
Ryan Graham

Có những nhược điểm của phương pháp này (được tóm tắt ở đây stackoverflow.com/a/881014/1116674 ). Một cách khác là tạo các nhánh cho mọi tính năng bạn đang làm việc và một worknhánh. Sau đó, bạn hợp nhất các nhánh cụ thể vào masterđể bạn không mất lịch sử trên chúng. Khi làm việc với work, bạn hợp nhất tất cả các chi nhánh của bạn vào nó. Đó là chi phí cao hơn, nhưng có thể có giá trị trong một số trường hợp.
Hudon

16

Theo mặc định, git-đẩy đẩy tất cả các chi nhánh. Khi bạn làm điều này:

 git checkout HEAD~3  #set head to three commits ago
 git push #attempt push from that head

Bạn di chuyển đến một ĐẦU tách ra (bạn không ở trên bất kỳ nhánh nào) và sau đó bạn đẩy tất cả các nhánh, bao gồm cả chủ địa phương (vẫn còn ở đó) đến chủ từ xa.

Giải pháp thủ công là:

 git push origin HEAD:master

Nếu bạn thấy hành vi mặc định đẩy tất cả các nhánh khó hiểu (và nguy hiểm!), Hãy thêm hành động này vào ~ / .gitconfig:

 [remote.origin]
    push = HEAD

Sau đó, chỉ có chi nhánh bạn đang được đẩy. Trong ví dụ của bạn (một cái đầu tách ra), bạn sẽ nhận được thông báo lỗi này, thay vì vô tình đẩy các cam kết sai:

 error: unable to push to unqualified destination: HEAD

10

Câu trả lời ngắn:

git push <latest commit SHA1 until you want commits to be pushed>

Ví dụ:

git push fc47b2

git push HEAD~2

Câu trả lời dài:

Các cam kết được liên kết với nhau như một chuỗi với cơ chế cha / con. Do đó, việc đẩy một cam kết thực sự cũng đẩy tất cả các cam kết cha mẹ vào cam kết này mà không biết đến điều khiển từ xa. Điều này được thực hiện hoàn toàn khi bạn git pushcam kết hiện tại: tất cả các cam kết trước đó cũng được đẩy vì lệnh này tương đương với git push HEAD.

Vì vậy, câu hỏi có thể được viết lại thành Cách đẩy một cam kết cụ thể và ví dụ như cam kết cụ thể này có thể là ĐẦU ~ 2.

Nếu các cam kết bạn muốn đẩy không liên tục, chỉ cần sắp xếp lại chúng với một git rebase -ilần đẩy cụ thể .


5

1) Sử dụng "git rebase" để sắp xếp lại các cam kết của bạn, nếu bạn muốn.

git rebase -i

Lệnh này sẽ hiển thị một cái gì đó như thế này trong trình soạn thảo của bạn (Tôi đang sử dụng vim)

pick 4791291 commitA
pick a2bdfbd commitB
pick c3d4961 commitC
pick aa1cefc commitD
pick 9781434 commitE

# Rebase ..............
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out




^G Get Help         ^O WriteOut         ^R Read File        ^Y Prev Page                ^K Cut Text         ^C Cur Pos
^X Exit             ^J Justify          ^W Where Is         ^V Next Page            ^U UnCut Text       ^T To Spell

2) Sắp xếp lại các cam kết của bạn theo sự lựa chọn của bạn bằng cách cắt dán đơn giản. Giả sử thứ tự mới là

cam kết34

chọn cam kết c3d4961

chọn 4791291 cam kết

chọn aa1cefc commitD

chọn a2bdfbd commitB

Thực hiện những thay đổi này trong trình chỉnh sửa của bạn và nhấn ctrl + O (writeOut)

Hoặc bạn cũng có thể sử dụng

git rebase -i HEAD~<commitNumber>

Bạn có thể kiểm tra trình tự mới với

git log

3) Bây giờ sử dụng

git push <remoteName> <commit SHA>:<remoteBranchName>

Nếu chỉ có một nhánh ở xa (nguồn gốc) và một nhánh tại địa phương (chính), chỉ cần sử dụng

git push <commit SHA>
git push aa1cefc

Điều này sẽ đẩy commitB và commitD.

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.