Git reset - giữ và đẩy vào kho lưu trữ từ xa


200

Tôi đã có một kho lưu trữ có một số cam kết xấu về nó (D, E và F cho ví dụ này).

ABCDEF chính và nguồn gốc / chủ

Tôi đã sửa đổi kho lưu trữ cục bộ cụ thể bằng a git reset --hard. Tôi đã lấy một nhánh trước khi thiết lập lại để bây giờ tôi có một repo trông giống như:

A-B-C master  
     \ D-E-F old_master

A-B-C-D-E-F origin/master

Bây giờ tôi cần một số phần của các cam kết xấu đó vì vậy tôi đã chọn các bit tôi cần và thực hiện một số cam kết mới để bây giờ tôi có các mục sau:

A-B-C-G-H master
     \ D-E-F old_master

Bây giờ tôi muốn đẩy tình trạng này đến repo từ xa. Tuy nhiên, khi tôi cố gắng thực hiện một git pushGit lịch sự đưa cho tôi bàn chải:

$ git push origin +master:master --force  
Total 0 (delta 0), reused 0 (delta 0)  
error: denying non-fast forward refs/heads/master (you should pull first)  
To git@git.example.com:myrepo.git  
! [remote rejected] master -> master (non-fast forward)  
error: failed to push some refs to 'git@git.example.com:myrepo.git'  

Làm cách nào để có được repo từ xa để lấy trạng thái hiện tại của repo cục bộ?


2
Đây là một bản sao 'gần như' của một số "làm thế nào để tôi đẩy các câu hỏi lịch sử đã sửa đổi", ví dụ: xem câu trả lời ở đây stackoverflow.com/questions/253055/ mẹo
CB Bailey

2
Điều đó đúng và tôi đã tìm kiếm StackOverflow để tìm câu trả lời trước khi đăng. Tuy nhiên, tìm kiếm của tôi chỉ đưa ra câu trả lời trong đó một lực đẩy git đã khắc phục vấn đề. Cảm ơn bạn đã liên kết đến bài đăng của bạn :)
robertpostill

2
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

Câu trả lời:


287

Nếu việc đẩy một lực đẩy không giúp ích (" git push --force origin" hoặc " git push --force origin master" là đủ), điều đó có nghĩa là máy chủ từ xa đang từ chối các lần đẩy không chuyển tiếp nhanh thông qua biến cấu hình receive.denyNonFastForwards (xem phần mô tả git config để mô tả), hoặc thông qua cập nhật / móc nhận trước.

Với Git cũ hơn, bạn có thể khắc phục hạn chế đó bằng cách xóa " git push origin :master" (xem ':' trước tên chi nhánh) và sau đó tạo lại " git push origin master" chi nhánh đã cho.

Nếu bạn không thể thay đổi điều này, thì giải pháp duy nhất sẽ là thay vì viết lại lịch sử để tạo ra một thay đổi hoàn nguyên cam kết trong DEF :

ABCDEF - [(DEF) ^ - 1] chính

Nguồn gốc ABCDEF / chủ

2
@ JakubNarębski, cảm ơn. get revert HEAD~Nđã giúp. Nlà số lượng cam kết. Ví dụ: nếu tôi cần cam kết trước đó, tôi sẽ sử dụnggit revert HEAD~1
Maksim Dmitriev

1
Và lưu ý rằng bạn sẽ phá vỡ mọi chủ nhân địa phương khác bằng cách làm điều này.
Tom Brito

24

Để bổ sung cho câu trả lời của Jakub, nếu bạn có quyền truy cập vào máy chủ git từ xa trong ssh, bạn có thể vào thư mục git remote và đặt:

user@remote$ git config receive.denyNonFastforwards false

Sau đó quay lại repo địa phương của bạn, thử lại để thực hiện cam kết của bạn với --force:

user@local$ git push origin +master:master --force

Và cuối cùng hoàn nguyên cài đặt của máy chủ ở trạng thái được bảo vệ ban đầu:

user@remote$ git config receive.denyNonFastforwards true

Xem thêm pete.akeo.ie/2011/02/denying-non-fast-forward-and.html để biết thông tin phù hợp với sourceforge về điều này.
hlovdal

Hướng dẫn chi tiết về cách tắt denyNonFastForwards bằng cách sử dụng viđược cung cấp trên bài đăng SO này: stackoverflow.com/a/43721579/2073804
ron190

2

Thay vì sửa nhánh "chủ" của bạn, cách dễ dàng hơn để trao đổi nó với "chủ mong muốn" của bạn bằng cách đổi tên các nhánh. Xem https://stackoverflow.com/a/2862606/2321594 . Bằng cách này, bạn thậm chí sẽ không để lại bất kỳ dấu vết của nhiều bản ghi hoàn nguyên.


1

Toàn bộ git đặt lại doanh nghiệp nhìn xa làm phức tạp cho tôi.

Vì vậy, tôi đã làm một cái gì đó dọc theo dòng để có được thư mục src của tôi trong trạng thái tôi đã có một vài cam kết trước đây

# reset the local state
git reset <somecommit> --hard 
# copy the relevant part e.g. src (exclude is only needed if you specify .)
tar cvfz /tmp/current.tgz --exclude .git  src
# get the current state of git
git pull
# remove what you don't like anymore
rm -rf src
# restore from the tar file
tar xvfz /tmp/current.tgz
# commit everything back to git
git commit -a
# now you can properly push
git push

Bằng cách này, trạng thái của các vấn đề trong src được giữ trong một tệp tar và git buộc phải chấp nhận trạng thái này mà không cần quá nhiều vấn đề về cơ bản thư mục src được thay thế bằng trạng thái mà nó đã thực hiện trước đó.


0

Đối với người dùng GitHub, điều này hiệu quả với tôi:

  1. Trong bất kỳ quy tắc bảo vệ chi nhánh nào mà bạn muốn thực hiện thay đổi, hãy đảm bảo Cho phép đẩy lực được bật
  2. git reset --hard <full_hash_of_commit_to_reset_to>
  3. git push --force

Điều này sẽ "sửa" lịch sử chi nhánh trên máy cục bộ của bạn và máy chủ GitHub, nhưng bất kỳ ai đã đồng bộ hóa chi nhánh này với máy chủ vì cam kết xấu sẽ có lịch sử trên máy cục bộ của họ. Nếu họ có quyền đẩy trực tiếp đến chi nhánh thì những cam kết này sẽ hiển thị ngay khi họ đồng bộ hóa.

Tất cả những gì người khác cần làm là git resetlệnh từ phía trên để "sửa" nhánh trên máy cục bộ của họ. Tất nhiên họ sẽ cần phải cảnh giác với bất kỳ cam kết địa phương nào được thực hiện cho chi nhánh này sau khi băm mục tiêu. Cherry chọn / sao lưu và áp dụng lại những thứ đó khi cần thiết, nhưng nếu bạn ở trong một chi nhánh được bảo vệ thì số người có thể cam kết trực tiếp với nó có thể bị hạn chế.

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.