2013-07-30 25 views
7

Giả sử tôi có kho lưu trữ có số sửa đổi A. Và tôi muốn cập nhật nó thành bản sửa đổi B, trong khi sửa đổi mới nhất là C. (bản sửa đổi A sớm hơn B và B sớm hơn C). Tôi mới đến git vì vậy tôi đã làm một số nghiên cứu, và tìm thấy this, trong đó truyền cảm hứng cho tôi một giải pháp:Git: cập nhật một kho lưu trữ để sửa đổi nhất định

git pull # update from A to the latest revision C 
git reset --hard B 

Điều đó có hiệu quả. Nhưng kể từ khi tôi không thể git reset --hard B từ A trực tiếp, các tiền lệ cập nhật mới nhất vẫn còn quá nặng, tôi tự hỏi có thể có một số lệnh một dòng để phù hợp với nhu cầu của tôi. Bất kỳ gợi ý xin vui lòng?

Trả lời

14

Không có "cập nhật kho lưu trữ cho một phiên bản nhất định". Kho lưu trữ của bạn có tất cả các phiên bản, đó là những gì git fetch/git pull thực hiện.

Nếu bạn muốn đặt một bản sửa đổi cụ thể của repo trong cây làm việc hiện tại của bạn tại địa phương, có một số cách bạn có thể làm điều đó. Gần nhất với câu hỏi của bạn là:

cập nhật kho địa phương của bạn:

git fetch origin 

tạo ra một chi nhánh mới (từ bất cứ chi nhánh bạn đang hiện trên, chúng tôi sẽ thiết lập lại cứng sau để nó không quan trọng):

git branch yourbranchname 
git checkout yourbranchname 

2 hoạt động trên có thể được viết tắt thành một (HEAD hiện nay được giả định là nguồn gốc của chi nhánh):

git checkout -b yourbranchname 

sau đó đặt con trỏ của chi nhánh đó để cam kết bạn cần (B):

git reset --hard sha1-of-B 

git reset --hard sẽ luôn luôn làm việc, nó không phụ thuộc vào lịch sử của chi nhánh, điều kiện duy nhất cho nó công việc là cam kết B là trong bạn cơ sở đối tượng địa phương (tức là B phải tồn tại và phải được lấy từ một repo từ xa là nó không phải là công việc của bạn).

Như @Hasturkun chỉ ra, bạn cũng có thể chi nhánh trực tiếp từ bất kỳ băm tùy ý với một cuộc tranh cãi thêm:

git checkout -b yourbranchname SHA-1 
+4

Bạn không thực sự cần phải thiết lập lại chi nhánh, vì bạn có thể chỉ cần tạo nó với điểm bắt đầu là nơi bạn muốn nó, tức là.'git branch newbranch SHA1-of-B', hoặc' git checkout -b newbranch SHA1-of-B' – Hasturkun

+0

@ Sébastien Cảm ơn bạn đã giới thiệu cho tôi một số khái niệm cơ bản. –

+0

@Hasturkun câu trả lời của bạn hoạt động hoàn hảo cho tôi, cảm ơn. –

5

Bạn cần sử dụng git checkout cho việc này. Chỉ cần làm:

git checkout B

và bạn sẽ có bản sửa đổi đó.

+0

Cảm ơn, nhưng tôi gặp lỗi khi sử dụng 'git checkout':' tham chiếu không phải là một cây'. –

+0

Tôi nghĩ rằng bình luận của Hasturkun trong câu trả lời của Sébastien Dawans sẽ giải quyết vấn đề đó cho bạn. – jbr

1

cách tiếp cận của bạn là hoàn toàn sai. Bạn đang sửa đổi một cái gì đó mà bạn không muốn sửa đổi: Chi nhánh hiện tại của bạn (có lẽ là master).

A, kho git tuyến tính đơn giản là một chuỗi các cam kết như thế này

*---A---*---*---B---*---*---C 
    ^     ^
    |      | 
    master    origin/master 
    ^
    | 
    HEAD 

Đó là tình trạng của kho lưu trữ của bạn sau khi bạn đã gọi git fetch. Lưu ý rằng toàn bộ lịch sử với tất cả các bước trung gian của nó nằm ngay trên ổ đĩa cứng cục bộ của bạn. Chỉ là bạn chỉ có trạng thái tại cam kết A được kiểm tra (HEAD điểm đến master trỏ đến A), vì vậy các tệp mà bạn thấy thuộc về trạng thái đó.

Bây giờ, nếu bạn chỉ muốn xem trạng thái đã cam kết là B, bạn có thể chỉ cần kiểm tra cam kết đó với git checkout B. Này cập nhật các tập tin mà bạn nhìn thấy trạng thái của B, và chỉ HEAD tại đó cam kết:

*---A---*---*---B---*---*---C 
    ^  ^  ^
    |   |   | 
    master  HEAD origin/master 

HEAD luôn tham chiếu cam/chi nhánh git nghĩ bạn đang ở trên, và mà nó sẽ so sánh thư mục làm việc của bạn khi bạn gọi git status.

Đơn giản git checkout B là đủ, nếu bạn chỉ muốn xem xét cam kết đó. Nếu bạn thực sự muốn thực hiện các thay đổi mà bạn cam kết và muốn giữ lại, bạn nên giới thiệu một nhánh mới để ghi lại những thay đổi đó. Điều này đạt được bằng một đơn giản git checkout -b newBranch sau git checkout B. Điều này sẽ cung cấp cho bạn trạng thái

*---A---*---*---B---*---*---C 
    ^  ^  ^
    |   |   | 
    master newBranch origin/master 
       ^
       | 
       HEAD 

Điều này chỉ cung cấp tên để cam kết B ngoài giá trị băm của nó. Sau khi một số cam kết hơn, trạng thái của bạn sẽ trông như thế này:

*---A---*---*---B---*---*---C 
    ^   |   ^
    |   |   | 
    master   \ origin/master 
        \ 
        *---*---D 
          ^
          | 
         newBranch 
          ^
          | 
          HEAD 

Vấn đề là, rằng, sau khi kiểm tra ra một số chi nhánh khác/cam kết với git checkout ..., bạn luôn có thể dễ dàng trở lại cam kết D bằng cách gọi git checkout newBranch, và tham chiếu cố định dừng lại git từ cam kết thu gom rác D.


Bây giờ, tại sao sử dụng git reset --hard không hợp lệ? Trước hết, nó thay đổi tất cả các thay đổi cục bộ mà bạn chưa cam kết mà không cần thông báo thêm. Thứ hai, nó có thể làm bạn mất lịch sử nếu bạn không cẩn thận với nó. Ví dụ:

Hãy xem xét trường hợp bạn đã thực hiện một số thay đổi sau lần cuối cùng bạn đẩy tới kho lưu trữ ngược dòng của mình và muốn xem xét một số cam kết lịch sử B. (. Hơi ngược lại của vụ án trong câu hỏi của bạn) Lịch sử trông như thế này:

*---A---*---*---B---*---*---C 
    ^     ^
     |      | 
origin/master    master 
          ^
           | 
          HEAD 

Với git reset --hard B, bạn sẽ có được trạng thái này:

*---A---*---*---B-(-*---*---C) 
    ^  ^
     |   | 
origin/master master 
       ^
        | 
       HEAD 

Các cam kết trong ngoặc đơn không được tham chiếu trực tiếp hoặc gián tiếp bởi bất kỳ chi nhánh nào nữa, và có thể là rác thu thập bất cứ lúc nào. git có thể không quá tích cực với việc thu thập rác, nhưng nếu thu gom rác trong khi bạn ở trạng thái này, không có cách nào để bạn nhận được cam kết C trở lại, nó sẽ bị mất mãi mãi. Bạn không muốn điều này xảy ra, do đó, không nên để thói quen sử dụng git reset --hard một cách nhẹ nhàng.

Nếu bạn đã sử dụng git checkout thay vào đó, chi nhánh master sẽ vẫn trỏ vào C và bạn vẫn có thể quay lại với đơn giản git checkout master.

Các vấn đề liên quan