Làm cách nào để đẩy cam kết sửa đổi vào kho Git từ xa?


662

Khi tôi làm việc một chút với mã nguồn của mình, tôi đã thực hiện cam kết thông thường 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 mình trong mã nguồn. Vì vậy, tôi thực hiện lệnh sửa đổi để thay thế cho cam kết trước đó:

> git commit --amend

Thật không may, 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.)


Điều gì xảy ra nếu --amend của tôi chỉ thay đổi thông điệp cam kết? Bất kỳ cách nào để chỉnh sửa thông điệp cam kết cuối cùng một mình, nếu nó đã được đẩy đến từ xa? Tôi đã làm điều đó trên Github và nhận được thông điệp tương tự về việc không chuyển tiếp nhanh. Sau đó, tôi đã áp dụng một giải pháp bên dưới nhưng việc hợp nhất chỉ cần thêm nhiều thông điệp cam kết ở trên ..

7
@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 cam kết, do đó, nó sẽ 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ú (vì vậy 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

1
Bạn sẽ sớm (git1.8.5, Q4 2013) có thể làm một git push -forcecách cẩn thận hơn .
VonC

3
Đâ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 sửa đổi git trước đó. Chỉ cần thêm một số mã giữ chỗ, ý tôi là, Thêm một số nhận xét, Dọn sạch một chút mã hoặc chỉ cần thêm một vài dấu gạch ngang dash dash .... Bây giờ hãy thực hiện một cam kết thực sự và đẩy nó vào điều khiển từ xa. Làm xong !
nehem

@ user58777 Nếu -amend của bạn chỉ để thay đổi thông điệp cam kết và bạn đã không thực hiện thêm bất kỳ cam kết cục bộ nào kể từ đó, bạn có thể đặt lại chi nhánh địa phương của mình thành cam kết từ xa mà bạn đã đẩy trước khi sửa đổi tin nhắn cam kết.
Scott Ahten

Câu trả lời:


504

Tôi thực sự đã từng bị đẩy --force.gitlưu trữ và bị Linus BIG TIME mắng . 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 dù sao, 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 sau tình huống sau khi bạn đã đẩy ra cam kết sửa đổi với --force (hoặc + master).

  1. Sử dụng git reflogđể tìm cam kết cũ mà bạn đã sửa đổi (gọi nó 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 một sự hợp nhất giữa oldnew, ghi lại cây new, như git checkout new && git merge -s ours old.
  3. Hợp nhất với chủ của bạn với git merge master
  4. Cập nhật chủ của bạn với kết quả với git push . HEAD:master
  5. Đẩy kết quả ra.

Sau đó, những người đã đủ bất hạnh đã dựa trên công việc của họ cam kết bạn xóa sạch bằng cách sửa đổi và buộc một push sẽ thấy những kết quả hợp nhất sẽ thấy rằng bạn ủng hộ newhơn old. Sự hợp nhất sau này của họ sẽ không thấy xung đột giữa oldnewkết quả từ việc sửa đổi của bạn, vì vậy họ không phải chịu đựng.


17
Tôi biết rất rõ điều gì xảy ra khi bạn buộc đẩy 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 trên ổ đĩa mạng nên nó không phải là vấn đề lớn. Tôi chưa bao giờ nghĩ về việc hợp nhất một cam kết sửa đổi, vì vậy tôi sẽ nêu lên điều này.
Spoike

61
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 nhánh tính năng được phát triển bởi các cá nhân.
Ondra Žižka

2
Những lời trách mắng từ Linus là vì bạn đã xóa lịch sử bằng tùy chọn bắt buộc chứ 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, vì nó không thay đổi lịch sử.
dùng411279

2
Xin vui lòng, vì tác giả (gitster) của câu trả lời này dường như không còn tồn tại nữa, bất cứ ai cũng có thể giúp làm rõ mục số 1: tìm cam kết cũ. Nếu bạn không có bản sao lưu, bạn sẽ tìm thấy nó ở đâu? Sửa đổi và lực đẩy sẽ không bị phá hủy? Có lẽ anh ta đang đề cập để có được nó từ một người bạn / cộng tác viên vẫn còn nó trong cây?
Bác sĩ Beco

2
Tiến sĩ Breco bạn có thể sử dụng git reflogđể tìm thấy nó
Simon Zyx

269

Bạn đang thấy một tính năng an toàn Git. Git từ chối cập nhật chi nhánh từ xa với chi nhánh của bạn, bởi vì cam kết đầu chi 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 chi 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 xuất hiện cùng một lúc và bất cứ ai đẩy cuối cùng sẽ mất công việc của người đẩy trước mà không có một trong hai họ nhận ra điều này.

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

git push -f origin master

Ngay cả điều này có thể không hoạt động vì Git cho phép các kho lưu trữ từ xa từ chối các lần đẩy không nhanh chóng ở phía 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ẽ giố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 là một bản hack bẩn, bạn có thể xóa và tạo lại nhánh, do đó:

git push origin :master
git push origin master

Nói chung, tham số cuối cùng để git pushsử dụng định dạng <local_ref>:<remote_ref>, trong đó local_reflà tên của nhánh trên kho lưu trữ cục bộ và remote_reflà tên của nhánh trên kho lưu trữ từ xa. Cặp lệnh này sử dụng hai tốc ký. :mastercó một local localf có nghĩa là đẩy một nhánh null sang phía từ xa master, tức là xóa nhánh từ xa. Tên nhánh không có :nghĩa là đẩy nhánh cục bộ có tên đã cho sang nhánh từ xa có cùng tên. mastertrong tình huống này là viết tắt của master:master.


2
Điều này không hoạt động với github, nó đã cho tôi thông báo sau: [từ chối] chủ (xóa chi nhánh hiện tại bị cấm)
vedang

Tôi không muốn thúc ép (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ó lựa chọn nào khác.
vedang

1
Đây là giải pháp duy nhất hoạt động cho repo của tôi được lưu trữ với lắp ráp.
Justin

1
xóa nhánh chủ từ xa sẽ giải phóng không gian trong repo từ xa?
Mr_and_Mrs_D

1
@Mr_and_Mrs_D: Không phải ngay lập tức, nhưng sau một git gclần các reflog đã hết hạn, các đối tượng cũ sẽ bị cắt xén. Không ai nhân bản kho lưu trữ sẽ nhận được bất kỳ đối tượng nào không còn truy cập được ngay khi chi nhánh được cập nhật.
CB Bailey

211

Rant nhanh: Việc không ai đăng câu trả lời đơn giản ở đây chứng tỏ sự thù địch của người dùng tuyệt vọng được thể hiện bởi Git CLI.

Dù sao, cách "rõ ràng" để làm điều này, giả sử bạn chưa cố gắng đẩy, là kéo trước. Điều này kéo theo sự thay đổi mà bạn đã sửa đổi (và vì vậy không còn nữa) để bạn có lại nó.

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

Vì thế:

git pull

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

Và sau

git push

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


2
Có, tôi đã viết về điều này, xem stackoverflow.com/questions/253055/iêu ;)
Spoike

10
Điều này không thực sự hoạt động như tôi mong đợi. Nó tạo ra hai cam kết mới. Một cái là bản sao của cái cũ, nhưng với những thay đổi được sửa đổi. Và một cam kết hợp nhất với một khác biệt trống. Vẫn không thay đổi cam kết cũ, tiết lộ dữ liệu nhạy cảm mà tôi đang cố gắng sửa đổi. Tôi tin git push -fhoặc git resetlà cách duy nhất để đi đến đây.
thnee

40
Trong khi về mặt 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 ra một cam kết bổ sung, 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 người đăng đã 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ẽ có ý nghĩa nhiều như không sửa đổi cam kết ở nơi đầu tiên.
Dan Jones

102

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

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

$ git push origin +master:master

+Dấu hiệu hàng đầu sẽ buộc sự thúc đẩy xảy ra, ngay cả khi nó không dẫn đến một cam kết "chuyển tiếp nhanh". (Một cam kết chuyển tiếp nhanh xảy ra khi những 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 khai.)


5
Làm thế nào điều này khác nhau (tốt hơn hoặc tồi tệ hơn) git đẩy -f? Cảm ơn!
bentford

11
@bentford: Về cơ bản nó giống như git push -f.
mipadi

54

Đây là một cách rất đơn giản và rõ ràng để 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

Mà làm như sau:

  • Đặt lại đầu chi nhánh để cam kết cha mẹ.
  • Stash cam kết cuối cùng này.
  • Lực đẩy từ xa. Điều khiển từ xa bây giờ không có cam kết cuối cùng.
  • Pop stash của bạn.
  • Cam kết sạch sẽ.
  • Đẩy vào điều khiển từ xa.

Nhớ thay đổi "nguồn gốc" và "chính chủ" nếu áp dụng điều này cho một chi nhánh hoặc điều khiển từ xa khác.


3
2 lưu ý: - đảm bảo thay đổi tên của chi nhánh nếu bạn đang làm việc trên một chi nhánh khác - Tôi phải sử dụng git addtrước khi cam kết bao gồm các thay đổi.
SylvainB

1
Trong Windows CMD, lệnh đầu tiên sẽ được thoát : git reset --soft "HEAD^". Phần còn lại hoạt động tốt.
MrMister

2
"Một cách rất đơn giản và sạch sẽ .." cit. Thủ tục này bao gồm đẩy cưỡng bức. Trong ánh sáng của tất cả những người bị bệnh trong Câu trả lời ở trên, tôi không chắc liệu quy trình này có thực sự là một quy trình sạch hay không.
Na13-c

24

Tôi đã giải quyết nó bằng cách loại bỏ cam kết sửa đổi cục bộ 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

2
Đây là phiên bản đơn giản nhất!
mknaf

Thêm một cam kết 'thay đổi' khác tốt hơn là làm rối tung lịch sử viết lại. Tôi đồng ý với @mknaf
sdkks

8

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
  • Đã thử đẩy, gặp lỗi, hoảng loạn, sáp nhập từ xa, có rất nhiều tập tin không phải của tôi, bị đẩy, thất bại, v.v.

Là một người mới chơi Git, tôi nghĩ rằng nó đã hoàn thành FUBAR .

Giải pháp: Giống như @bara đã đề xuất + tạo chi nhánh sao lưu 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ó thể đó không phải là một giải pháp nhanh chóng và sạch sẽ, và tôi đã mất lịch sử của mình (1 cam kết thay vì 5), nhưng nó đã cứu một ngày làm việc.


6

Nếu bạn chưa đẩy mã đến chi nhánh từ xa (GitHub / Bitbucket), bạn có thể thay đổi thông báo cam kết trên dòng lệnh như dưới đây.

 git commit --amend -m "Your new message"

Nếu bạn đang làm việc trên một chi nhánh cụ thể, hãy làm điều này:

git commit --amend -m "BRANCH-NAME: new message"

Nếu bạn đã đẩy mã với một thông báo sai thì bạn cần cẩn thận khi thay đổi tin nhắn. tức là sau khi bạn thay đổi thông điệp cam kết và thử đẩy nó một lần nữa, bạn sẽ gặp vấn đề. Để làm cho nó trơn tru làm theo các bước sau.

Xin vui lòng đọc toàn bộ câu trả lời trước khi làm điều đó

git commit --amend -m "BRANCH-NAME : your new message"

git push -f origin BRANCH-NAME                # Not a best practice. Read below why?

Lưu ý quan trọng: Khi bạn sử dụng lực đẩy trực tiếp, bạn có thể gặp phải các vấn đề về mã mà các nhà phát triển khác đang làm việc trên cùng một chi nhánh. Vì vậy, để tránh những xung đột đó, bạn cần phải rút mã từ chi nhánh của mình trước khi thực hiện lực đẩy :

 git commit --amend -m "BRANCH-NAME : your new message"
 git pull origin BRANCH-NAME
 git push -f origin BRANCH-NAME

Đây là cách tốt nhất khi thay đổi thông điệp cam kết, nếu nó đã được đẩy.


1
Nếu bạn đã rút lại thành công cam kết trong ví dụ trước, tại sao bạn cần phải đẩy? Sẽ không phải là một đẩy đủ tiêu chuẩn? Cảm ơn
Thomas

Câu hỏi của Thomas trong thực tế là rất hợp lệ. Bản thân tôi không cần phải thúc đẩy.
Na13-c

Xin đừng gọi đó là "cách tốt nhất" bởi vì có một cách để giải quyết --force, hãy xem câu trả lời được chấp nhận
Farid

5

Nếu bạn biết không ai rút được cam kết chưa sửa đổi của mình, hãy sử dụng --force-with-leasetùy chọn git push.

Trong TortoiseGit, bạn có thể thực hiện điều tương tự trong tùy chọn "Đẩy ..." "Buộc: Có thể loại bỏ" và kiểm tra "các thay đổi đã biết".

Buộc (Có thể loại bỏ các thay đổi đã biết) cho phép kho lưu trữ từ xa chấp nhận một lần đẩy không nhanh hơn an toàn hơn. Điều này có thể khiến kho lưu trữ từ xa bị mất các xác nhận; sử dụng nó một cách cẩn thận. Điều này có thể ngăn chặn mất các thay đổi không xác định từ những người khác trên điều khiển từ xa. Nó kiểm tra xem nhánh máy chủ có trỏ đến cùng một cam kết với nhánh theo dõi từ xa không (các thay đổi đã biết). Nếu có, một lực đẩy sẽ được thực hiện. Nếu không nó sẽ bị từ chối. Vì git không có thẻ theo dõi từ xa, thẻ không thể được ghi đè bằng tùy chọn này.


4

Bạn đang gặp lỗi này vì điều khiển từ xa Git đã có các tệp cam kết này. Bạn phải buộc đẩy chi nhánh để làm việc này:

git push -f origin branch_name

Ngoài ra, hãy đảm bảo bạn kéo mã từ xa vì người khác trong nhóm của bạn có thể đã bị đẩy đến cùng một chi nhánh.

git pull origin branch_name

Đây là một trong những trường hợp chúng ta phải buộc cam kết từ xa.


Tại sao câu trả lời này không được tính cho các ý kiến ​​chính được nêu ra trong các câu trả lời trước?
Na13-c

2

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

git push origin master -f

hoặc là:

git push origin master --force

Tôi nghe điều đó thật tệ, và tôi chắc chắn là như vậy. Có một lý do (tốt) cho git thất bại theo mặc định (và yêu cầu - lực lượng), tôi chắc chắn.
Rolf

1

Tôi đã phải khắc phục vấn đề này bằng cách kéo từ repo từ xa và xử lý các xung đột hợp nhất nảy sinh, cam kết và sau đó đẩy. Nhưng tôi cảm thấy như có một cách tốt hơn.


Không hẳn vậy. Vấn đề có thể là bạn chưa cập nhật bản sao cục bộ của mình từ repo từ xa. Git sẽ không thúc đẩy nó bởi vì bạn có thể phải đối phó với việc hợp nhất bằng tay. Trong câu trả lời khác của tôi, tôi có một lệnh (và giải thích) sẽ buộc một lực đẩy - nhưng hãy cẩn thận có thể xóa các thay đổi trong điều khiển từ xa.
mipadi

1

Tôi chỉ tiếp tục làm những gì Git bảo tôi làm. Vì thế:

  • Không thể đẩy vì cam kết sửa đổi.
  • Tôi làm một cái kéo như đề nghị.
  • Hợp nhất thất bại. vì vậy tôi sửa nó bằng tay.
  • Tạo một cam kết mới (được gắn nhãn "hợp nhất") và đẩy nó.
  • Có vẻ như để làm việc!

Lưu ý: Cam kết sửa đổi là cam kết mới nhất.


1
Tôi sẽ downvote, nếu tôi có nhiều điểm danh tiếng hơn, vì vậy tôi sẽ chỉ hỏi một cách lịch sự, đó là ai trong số những người đau khổ? Người sửa đổi? Một, người đã kéo và làm việc trên một chi nhánh với cam kết sửa đổi? Trước khi sửa đổi, hay sau nó? Tôi vừa xóa mọi sửa đổi của tôi vì tôi đã hiểu lầm bạn ... May mắn là không có nhiều ...
Bartis Áron

1

Những điều sau đây làm việc cho tôi khi thay đổi Tác giả và Người ủy quyền của một cam kết.

git push -f origin master

Git đủ thông minh để nhận ra rằng đây là những cam kết của các đồng bằng giống hệt nhau, chỉ khác nhau trong phần thông tin meta.

Cả người đứng đầu địa phương và từ xa chỉ vào các cam kết trong câu hỏi.



0

Ở đây, Làm thế nào tôi sửa một chỉnh sửa trong một cam kết trước đó:

  1. Lưu công việc của bạn cho đến nay.
  2. Bỏ các thay đổi của bạn ngay bây giờ nếu được thực hiện: git stash Bây giờ bản sao làm việc của bạn sạch sẽ ở trạng thái cam kết cuối cùng của bạn.
  3. Thực hiện các chỉnh sửa và sửa lỗi.
  4. Cam kết thay đổi trong chế độ "sửa đổi" :git commit --all --amend
  5. Trình chỉnh sửa của bạn sẽ xuất hiện để yêu cầu một thông điệp tường trình (theo mặc định, thông điệp tường trình cũ). Lưu và thoát khỏi trình chỉnh sửa khi bạn hài lòng với nó.

    Những thay đổi mới được thêm vào cam kết cũ. Xem cho chính mình với git loggit diff HEAD^

  6. Áp dụng lại các thay đổi được lưu của bạn, nếu được thực hiện: git stash apply

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.