Câu hỏi Làm cách nào để đẩy cam kết sửa đổi vào kho lưu trữ Git từ xa?


Khi tôi đã làm việc một chút với mã nguồn của tôi, tôi đã làm điều bình thường của tôi cam kết và sau đó tôi đẩy đến một kho lưu trữ từ xa. Nhưng sau đó tôi nhận thấy tôi quên tổ chức nhập khẩu của tôi trong mã nguồn. Vì vậy, tôi thực hiện lệnh sửa đổi để thay thế cam kết trước đó:

> git commit --amend

Thật không may là cam kết không thể được đẩy trở lại kho lưu trữ. Nó bị từ chối như thế này:

> git push origin
To //my.remote.repo.com/stuff.git/
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to '//my.remote.repo.com/stuff.git/'

Tôi nên làm gì? (Tôi có thể truy cập kho lưu trữ từ xa.)


526
2017-10-31 10:23


gốc


Điều gì xảy ra nếu tôi --amend chỉ thay đổi thông điệp cam kết? Bất kỳ cách nào để chỉnh sửa thông báo cam kết cuối cùng một mình, nếu nó đã được đẩy đến điều khiển từ xa? Tôi đã làm điều đó trên Github và nhận được cùng một thông điệp về không nhanh về phía trước. Sau đó, tôi đã áp dụng một giải pháp bên dưới nhưng hợp nhất vừa thêm nhiều thư cam kết lên trên cùng ..
@faB: Tôi nghĩ đó là một câu hỏi thường gặp. Một thông điệp cam kết được băm cùng với các cam kết, do đó, chaning nó thay đổi revid (băm). Nếu nó không rõ ràng: không bạn không thể. IIRC có thể lưu trữ thông tin ngoài băng trong ghi chú (để bạn có thể chú thích các cam kết hiện có mà không thay đổi chúng). Để gắn nhãn các cam kết cụ thể, hãy sử dụng thẻ - sehe
Bạn sẽ sớm (git1.8.5, Q4 2013) có thể làm một git push -force cẩn thận hơn. - VonC
Đây là phong cách cao bồi. Không tìm hiểu thêm hoặc không tìm cách để hoàn tác git trước đó sửa đổi. Chỉ cần thêm một số mã giữ chỗ, tôi có nghĩa là, Thêm một số bình luận, Dọn dẹp một chút mã hoặc chỉ cần thêm vài dấu gạch ngang gạch ngang dash .... Bây giờ thực hiện một cam kết thực sự và đẩy nó đến từ xa. Làm xong ! - nehemiah


Các câu trả lời:


Tôi thực sự đã từng bị đẩy --force và .git kho và bị mắng bởi Linus THỜI ĐIỂM TRỌNG ĐẠI. Nói chung điều này sẽ tạo ra rất nhiều vấn đề cho người khác. Một câu trả lời đơn giản là "Đừng làm điều đó".

Tôi thấy những người khác đã đưa ra công thức để làm như vậy anyway, vì vậy tôi sẽ không lặp lại chúng ở đây. Nhưng đây là một mẹo để phục hồi từ tình hình sau bạn đã đẩy ra cam kết sửa đổi với --force (hoặc + master).

  1. Tìm cam kết cũ mà bạn đã sửa đổi (gọi nó là oldvà chúng tôi sẽ gọi cam kết mới mà bạn đã tạo bằng cách sửa đổi new).
  2. Tạo hợp nhất giữa old và new, ghi lại cây new, như git checkout new && git merge -s ours old.
  3. Hợp nhất điều đó với chủ nhân của bạn git merge master
  4. Cập nhật tổng thể của bạn với kết quả bằng git push . HEAD:master
  5. Đẩy kết quả ra ngoài.

Sau đó, những người đã không may, đủ để có được dựa trên công việc của họ trên cam kết bạn bị xóa bởi sửa đổi và buộc một đẩy (đó là bạn là một cậu bé rất xấu) sẽ thấy kết quả hợp nhất sẽ thấy rằng bạn ủng hộ new kết thúc old. Các lần hợp nhất sau này sẽ không thấy xung đột giữa old và new kết quả từ việc sửa đổi của bạn, vì vậy họ không phải chịu đựng.


410
2018-01-11 07:36



Tôi rất ý thức điều gì sẽ xảy ra khi bạn ép buộc một cam kết sửa đổi (bằng cách phá hủy lịch sử). May mắn thay, tôi là nhà phát triển duy nhất trong dự án với repo từ xa đang ở trên một ổ đĩa mạng nên nó không phải là một vấn đề lớn. Tôi không bao giờ nghĩ đến việc sáp nhập một cam kết sửa đổi, vì vậy tôi sẽ upvote này. - Spoike
Linus đã nói gì? - Alan
Trong công ty của chúng tôi, chúng tôi buộc phải đẩy khá thường xuyên ... trên các chi nhánh được phát triển bởi các cá nhân. - Ondra Žižka
Lời trách mắng từ Linus là vì bạn đã xóa lịch sử với lựa chọn vũ lực, không phải vì bạn không nên làm điều đó. Giải pháp của GabrielleV hoạt động tốt, bởi vì nó không thay đổi lịch sử. - user411279
Về lực đẩy đến một chi nhánh tính năng, điều này không nên được coi là OK nếu bạn đang làm việc với một chi nhánh tính năng cá nhân? Lịch sử mà bạn ghi đè chỉ là của riêng bạn, mà bạn hoàn toàn nhận thức được. Ví dụ: các thành viên trong nhóm của tôi và tôi đặt tên cho các chi nhánh cá nhân tương ứng, ví dụ: develop_ <feature> _ <tên của nhà phát triển>. Đó là toàn bộ quan điểm của quản lý bản sửa đổi phân phối, chúng ta có thể làm bất cứ điều gì muốn ở trên hộp cát của riêng mình, chỉ cần không gây rối với lịch sử trong đường chính. - Joey Carson


Bạn đang nhìn thấy một tính năng an toàn Git. Git từ chối cập nhật nhánh từ xa với nhánh của bạn, vì cam kết của nhánh của bạn không phải là hậu duệ trực tiếp của cam kết đầu hiện tại của nhánh mà bạn đang đẩy tới.

Nếu đây không phải là trường hợp, thì hai người đẩy vào cùng một kho lưu trữ cùng một lúc sẽ không biết rằng có một cam kết mới đến cùng một lúc và bất cứ ai được đẩy cuối cùng sẽ mất công việc của máy nghiền trước đó mà không có họ nhận ra điều này.

Nếu bạn biết rằng bạn là người duy nhất đẩy và bạn muốn thúc đẩy một cam kết sửa đổi hoặc đẩy một cam kết mà gió trở lại chi nhánh, bạn có thể 'buộc' Git để cập nhật các chi nhánh từ xa bằng cách sử dụng -f công tắc điện.

git push -f origin master

Thậm chí điều này có thể không hoạt động khi Git cho phép các kho từ xa từ chối các cú đẩy không nhanh chóng ở đầu xa bằng cách sử dụng biến cấu hình receive.denynonfastforwards. Nếu đây là trường hợp lý do từ chối sẽ trông như thế này (lưu ý phần 'từ chối từ xa'):

 ! [remote rejected] master -> master (non-fast forward)

Để giải quyết vấn đề này, bạn cần phải thay đổi cấu hình của kho lưu trữ từ xa hoặc như một hack bẩn, bạn có thể xóa và tạo lại nhánh như sau:

git push origin :master
git push origin master

Nói chung tham số cuối cùng git push sử dụng định dạng <local_ref>:<remote_ref>, Ở đâu local_ref là tên của chi nhánh trên kho lưu trữ cục bộ và remote_ref là tên của chi nhánh trên kho lưu trữ từ xa. Cặp lệnh này sử dụng hai chữ viết tắt. :mastercó một local_ref null có nghĩa là đẩy một nhánh rỗng vào phía xa master, tức là xóa chi nhánh từ xa. Tên chi nhánh không có : có nghĩa là đẩy nhánh địa phương với tên đã cho đến nhánh từ xa có cùng tên. master trong tình huống này là viết tắt của master:master.


239
2017-10-31 21:58



điều này đã không làm việc với github, nó đã cho tôi thông báo sau: [từ xa bị từ chối] master (xóa chi nhánh hiện tại bị cấm) - vedang
Tôi không muốn ép buộc (mà tôi biết sẽ giải quyết vấn đề), nhưng bây giờ tôi đoán tôi không có sự lựa chọn. - vedang
Đây là giải pháp duy nhất làm việc cho repo của tôi được tổ chức với assembla. - Failpunk
việc xóa nhánh chủ từ xa sẽ giải phóng không gian trong repo từ xa? - Mr_and_Mrs_D
@Mr_and_Mrs_D: Không ngay lập tức, nhưng sau git gc một khi các reflog đã hết hạn các đối tượng cũ sẽ bị cắt xén. Không ai bắt chước kho lưu trữ sẽ nhận được bất kỳ đối tượng nào không thể truy cập được ngay khi nhánh đã được cập nhật. - CB Bailey


Nhanh chóng rant: Thực tế là không có ai đã đăng câu trả lời đơn giản ở đây cho thấy sự thù địch người dùng tuyệt vọng trưng bày bởi Git CLI.

Dù sao, cách "rõ ràng" để làm điều này, giả sử bạn đã không cố gắng ép buộc, là để kéo đầu tiên. Điều này kéo thay đổi mà bạn đã sửa đổi (và do đó không còn có) để bạn có nó một lần nữa.

Khi bạn đã giải quyết bất kỳ xung đột nào, bạn có thể đẩy lại.

Vì thế:

git pull

Nếu bạn nhận được lỗi trong kéo, có thể có điều gì đó sai trong cấu hình kho lưu trữ cục bộ của bạn (tôi đã có một lỗi không đúng trong phần nhánh .git / config).

Và sau

git push

Có lẽ bạn sẽ nhận được một cam kết thêm với chủ đề nói về một "hợp nhất tầm thường".


191
2017-10-31 14:35



Có, tôi đã viết về điều này, xem stackoverflow.com/questions/253055/… ;) - Spoike
Điều này không thực sự làm việc như tôi mong đợi nó. Nó tạo ra hai cam kết mới. Một trong đó là bản sao của cái cũ, nhưng với những thay đổi được sửa đổi. Và một hợp nhất cam kết với một khác biệt rỗng. Vẫn để nguyên cam kết cũ, tiết lộ những dữ liệu nhạy cảm mà tôi đang cố sửa đổi. tôi tin git push -f hoặc là git reset là cách duy nhất để đến đây. - thnee
Trong khi kỹ thuật trả lời vấn đề, nó không thực sự giải quyết vấn đề. Như bạn đã nói, nó sẽ tạo thêm một cam kết, nhưng lý do chính khiến mọi người sửa đổi một cam kết là tránh tạo ra một cam kết mới. Vì vậy, nếu áp phích được làm theo hướng dẫn của bạn, anh ta sẽ không nhận được kết quả mong muốn. Nó sẽ làm cho ý nghĩa nhiều để không sửa đổi các cam kết ở nơi đầu tiên. - Dan Jones


Câu trả lời ngắn gọn: Đừng đẩy các cam kết đã sửa đổi vào repo công khai.

Câu trả lời dài: Một vài lệnh Git, như git commit --amend và git rebase, thực sự viết lại biểu đồ lịch sử. Điều này là tốt miễn là bạn chưa xuất bản các thay đổi của bạn, nhưng một khi bạn làm, bạn thực sự không nên mucking xung quanh với lịch sử, bởi vì nếu ai đó đã có thay đổi của bạn, sau đó khi họ cố gắng kéo một lần nữa, nó có thể thất bại . Thay vì sửa đổi một cam kết, bạn chỉ cần thực hiện một cam kết mới với những thay đổi.

Tuy nhiên, nếu bạn thực sự, thực sự muốn thúc đẩy một cam kết sửa đổi, bạn có thể làm như thế này:

$ git push origin +master:master

Sự lãnh đạo + dấu hiệu sẽ buộc đẩy xảy ra, ngay cả khi nó không dẫn đến cam kết "chuyển tiếp nhanh". (Cam kết chuyển tiếp nhanh xảy ra khi các thay đổi bạn đang đẩy là hậu duệ trực tiếp của những thay đổi đã có trong repo công cộng.)


90
2018-06-21 14:41



Làm thế nào là khác nhau này (tốt hơn hay tệ hơn) so với git push -f? Cảm ơn! - bentford
@ bentford: Về cơ bản nó giống như git push -f. - mipadi


Đây là một cách rất đơn giản và sạch sẽ để thúc đẩy các thay đổi của bạn sau khi bạn đã thực hiện commit --amend:

git reset --soft HEAD^
git stash
git push -f origin master
git stash pop
git commit -a
git push origin master

Điều nào sau đây:

  • Đặt lại đầu nhánh thành cam kết gốc.
  • Stash cam kết cuối cùng này.
  • Buộc đẩy tới điều khiển từ xa. Điều khiển từ xa hiện không có cam kết cuối cùng.
  • Pop stash của bạn.
  • Cam kết sạch sẽ.
  • Đẩy tới điều khiển từ xa.

Hãy nhớ thay đổi "origin" và "master" nếu áp dụng điều này cho một nhánh khác hoặc từ xa.


36
2017-09-24 15:46



2 nhận xét: - hãy chắc chắn để thay đổi tên của chi nhánh nếu bạn đang làm việc trên một số khác - tôi đã phải sử dụng git add trước khi cam kết của tôi bao gồm các thay đổi. - SylvainB
Trong Windows CMD, lệnh đầu tiên phải được thoát: git reset --soft "HEAD^". Phần còn lại hoạt động tốt. - MrMister


Tôi đã giải quyết nó bằng cách loại bỏ cam kết đã sửa đổi tại địa phương của tôi và thêm các thay đổi mới lên trên:

# Rewind to commit before conflicting
git reset --soft HEAD~1

# Pull the remote version
git pull

# Add the new commit on top
git add ...
git commit
git push

21
2018-02-20 10:24



Đây là phiên bản đơn giản nhất! - mknaf
Thêm một cam kết 'thay đổi' khác là tốt hơn so với rối tung với lịch sử viết lại. Tôi đồng ý với @mknaf - sdkks


Tôi đã từng gặp vấn đề tương tự.

  • Vô tình sửa đổi cam kết cuối cùng đã được đẩy
  • Thực hiện rất nhiều thay đổi cục bộ, cam kết khoảng năm lần
  • Đã cố gắng để đẩy, có một lỗi, hoảng loạn, sáp nhập từ xa, có rất nhiều không-my-file, đẩy, thất bại, vv

Là một Git-newbie, tôi nghĩ rằng nó đã hoàn thành FUBAR.

Giải pháp: Hơi giống như @bara đề xuất + tạo một chi nhánh dự phòng cục bộ

# Rewind to commit just before the pushed-and-amended one.
# Replace <hash> with the needed hash.
# --soft means: leave all the changes there, so nothing is lost.
git reset --soft <hash>

# Create new branch, just for a backup, still having all changes in it.
# The branch was feature/1234, new one - feature/1234-gone-bad
git checkout -b feature/1234-gone-bad

# Commit all the changes (all the mess) not to lose it & not to carry around
git commit -a -m "feature/1234 backup"

# Switch back to the original branch
git checkout feature/1234

# Pull the from remote (named 'origin'), thus 'repairing' our main problem
git pull origin/feature/1234

# Now you have a clean-and-non-diverged branch and a backup of the local changes.
# Check the needed files from the backup branch
git checkout feature/1234-gone-bad -- the/path/to/file.php

Có lẽ nó không phải là một giải pháp nhanh chóng và sạch sẽ, và tôi bị mất lịch sử của tôi (1 cam kết thay vì 5), nhưng nó đã lưu một ngày làm việc.


7
2018-06-07 00:03