Git: Làm thế nào để rebase đến một cam kết cụ thể?


154

Tôi muốn phản đối một cam kết cụ thể, không phải là một TRƯỞNG của chi nhánh khác:

A --- B --- C          master
 \
  \-- D                topic

đến

A --- B --- C          master
       \
        \-- D          topic

thay vì

A --- B --- C          master
             \
              \-- D    topic

Làm thế nào tôi có thể đạt được điều đó?


4
Bạn đã thử làm git checkout Btrước khi chạy git rebasechưa?
Peter-Paul van Gemerden

Không, có nên giúp đỡ? Tôi đoán chỉ có các tài liệu tham khảo của rebaselệnh là những gì quan trọng.
Ondra ižka

Câu trả lời:


98

Bạn có thể tránh sử dụng tham số --onto bằng cách tạo một nhánh tạm thời trên cam kết bạn thích và sau đó sử dụng rebase ở dạng đơn giản:

git branch temp master^
git checkout topic
git rebase temp
git branch -d temp

5
Tôi thích cách tiếp cận giống RISC này hơn :) Sẽ thử. Cảm ơn.
Ondra ižka

10
Tôi tự hỏi tại sao nó không làm việc cho tôi, trong một kịch bản hơi khác . Tôi muốn nhóm bỏ qua pep8 và được dựa trên chủ . git rebase temp(khi ở trong nhóm ) từ bỏ với "Các nhóm chi nhánh hiện tại đã được cập nhật.".
Alois Mahdal

4
Giải pháp này sẽ không hoạt động đối với kịch bản mà chủ đề đã được chuyển sang chủ, nhưng bạn muốn phản hồi nó trên tổ tiên để làm chủ. Trong trường hợp đó, bạn phải sử dụng git rebase --onto <target> <from> <to>để bạn có thể chỉ định <từ> cam kết.
mirzmaster

Đây là tùy chọn dễ nhất nếu bạn muốn khởi động lại cùng một cam kết mà chi nhánh dựa trên.
Tro

1
Tôi dường như làm việc, nhưng GitLab nói điều khác. Nếu tôi có 10 lần cam kết phía sau, 5 lần cam kết phía trước, thì kỳ vọng của tôi là có 8 lần cam kết phía sau, 5 lần cam kết phía trước sau khi nhận được 2 lần cam kết. Nhưng thay vì điều này, nó bổ sung thêm nhiều cam kết cho những người 5.
ROMANIA_engineer

67

Bạn thậm chí có thể có một cách tiếp cận trực tiếp:

git checkout topic
git rebase <commitB>

6
Đối với tôi, điều này không thực sự làm những gì dự định. Theo như tôi có thể nói, nó cố gắng phản công lại "tổ tiên chung cuối cùng" của topiccommitB.
Dan Lenski

2
@DanLenski, đó không phải là cách rebase hoạt động. Trích dẫn các tài liệu , It works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn. tôi đã thử lại bây giờ và dường như hoạt động tốt.
r0hitsharma

1
Siêu dễ dàng và hoạt động! Bây giờ tôi có: commitB_from_master-> topicCommit1-> topicCommit2.
Martin Konicek

Nó không làm việc cho tôi. Trước đó, GitLab đã nói "n cam kết trước". Và bây giờ, nó nói "m cam kết phía trước" ở đâu m > n.
ROMANIA_engineer

48

Sử dụng tùy chọn "lên":

git rebase --onto master^ D^ D

2
DD^sẽ được băm của cam kết cuối cùng và tiếp theo của "chủ đề"?
Ondra ižka

39
Cú pháp là như thế git rebase --onto <new-parent> <old-parent>. Xem Đặt con trỏ cha git thành cha mẹ khác . Trong trường hợp của bạn, <new-
Parent

7
Tôi luôn sử dụng 3 đối số: từ bỏ, bắt đầu và kết thúc các cam kết để phản đối.
Adam Dymitruk

14
Điều này làm việc cho tôi:git rebase --onto <commit-ID> master

4
Nhận xét của @ jsz là chính xác, trái với nhận xét của Simon South, đó là cách khác: git rebase --onto master <commit-ID-of-old-parent>và cho OP git rebase --onto B A.
gabious

19

Nhận xét của jsz ở trên đã giúp tôi đỡ đau đớn, vì vậy đây là người nhận từng bước dựa trên nhận xét mà tôi đã sử dụng để phản đối / di chuyển bất kỳ cam kết nào lên trên bất kỳ cam kết nào khác:

  1. Tìm một điểm rẽ nhánh trước của nhánh cần được khởi động lại (di chuyển) - gọi nó là cha mẹ cũ. Trong ví dụ trên đó là A
  2. Tìm cam kết trên đầu trang mà bạn muốn chuyển chi nhánh sang - gọi nó là cha mẹ mới. Trong exampe đó là B
  3. Bạn cần phải ở trên chi nhánh của bạn (người bạn di chuyển):
  4. Áp dụng rebase của bạn: git rebase --onto <new parent> <old parent>

Trong ví dụ trên, đơn giản như:

   git checkout topic
   git rebase --onto B A

6
Đây phải là câu trả lời chính xác. Ngoại trừ việc tôi sử dụng git rebase --onto B master, hãy xem câu trả lời của tôi để được giải thích kỹ hơn.
Zack Morris

Nó không hoạt động tốt với tôi. Tôi đã chọn 2 lần xác nhận liên tiếp (lần cuối cùng từ chủ trong nhánh hiện tại và lần đầu tiên từ chủ không thuộc nhánh hiện tại). Tôi bắt đầu với 100 phía sau - 10 phía trước và thay vì có 99 phía sau - 10 phía trước, bây giờ tôi có 105 phía sau - 13 phía trước.
ROMANIA_engineer

Rất tiếc khi biết nó không hoạt động. Âm thanh như các nhánh của bạn chuyển hướng khá nhiều - Tôi khuyên bạn nên đập đầu tiên trước khi cố gắng chống lại các nhánh có nhiều điểm khác biệt này.
Nestor Milyaev

11

Giải pháp chủ đề

Lệnh chính xác để trả lời câu hỏi được đăng có thể là bất kỳ câu nào sau đây (giả sử chi nhánh topicđã được kiểm tra):

git rebase --onto B master
git rebase --onto master~1 master
git rebase --onto B A
git rebase --onto B C
git rebase --onto B

Nếu topickhông được kiểm tra, bạn chỉ cần thêm topicvào lệnh (ngoại trừ lệnh cuối cùng) như vậy:

git rebase --onto B master topic

Ngoài ra, hãy kiểm tra chi nhánh đầu tiên với:

git checkout topic

Khởi động lại bất kỳ chuỗi cam kết nào đối với một cam kết đích

Hình thức cơ bản của lệnh chúng ta cần, được lấy từ tài liệu, là:

git rebase --onto <Target> [<Upstream> [<Branch>]]

<Branch>là tùy chọn và tất cả những gì nó làm là kiểm tra nhánh được chỉ định trước khi thực hiện phần còn lại của lệnh. Nếu bạn đã kiểm tra chi nhánh bạn muốn bắt đầu, thì bạn không cần điều này. Lưu ý rằng bạn phải có chỉ định <Upstream>để chỉ định <Branch>hoặc git sẽ nghĩ rằng bạn đang chỉ định<Upstream> .

<Target>là cam kết chúng tôi sẽ đính kèm chuỗi cam kết của chúng tôi. Khi cung cấp một tên chi nhánh, bạn chỉ cần xác định cam kết đầu của chi nhánh đó. <Target>có thể là bất kỳ cam kết nào sẽ không được chứa trong chuỗi các cam kết được di chuyển. Ví dụ:

A --- B --- C --- D         master
      \
       \-- X --- Y --- Z    feature

Để di chuyển toàn bộ chi nhánh tính năng, bạn không thể chọn X, Y, Z, hoặc feature<Target> vì những tất cả đều cam kết bên trong nhóm được di chuyển.

<Upstream>là đặc biệt bởi vì nó có thể có nghĩa là hai điều khác nhau. Nếu đó là một cam kết là tổ tiên của nhánh đã kiểm tra, thì nó đóng vai trò là điểm cắt. Trong ví dụ tôi cung cấp, đây sẽ là bất cứ điều gì đó là không C, Dhoặc master. Tất cả các cam kết sau<Upstream> cho đến khi người đứng đầu chi nhánh kiểm tra là những người sẽ được di chuyển.

Tuy nhiên, nếu <Upstream>không phải là tổ tiên, thì hãy sao lưu chuỗi từ cam kết đã chỉ định cho đến khi tìm thấy tổ tiên chung với nhánh đã kiểm tra (và hủy bỏ nếu không thể tìm thấy chuỗi). Trong trường hợp của chúng tôi, một <Upstream>số B, C, D, hoặc mastertất cả sẽ kết quả trong cam kết Bphục vụ như là điểm cắt. <Upstream>bản thân nó là một lệnh tùy chọn và nếu nó không được chỉ định, thì git nhìn vào cha mẹ của nhánh đã kiểm tra, tương đương với việc nhậpmaster .

Bây giờ git đã chọn các cam kết nó sẽ cắt và di chuyển, nó áp dụng chúng để <Target>bỏ qua bất kỳ cái nào đã được áp dụng cho mục tiêu.

Ví dụ và kết quả thú vị

Sử dụng điểm bắt đầu này:

A --- B --- C --- D --- E         master
            \
             \-- X --- Y --- Z    feature
  • git rebase --onto D A feature
    Sẽ áp dụng cam kết B, C, X, Y, Zcam kết Dvà kết thúc bỏ qua BCvì họ đã có được áp dụng.

  • git rebase --onto C X feature
    Sẽ áp dụng các cam kết YZđể cam kết C, xóa hiệu quả cam kếtX


4

Một giải pháp đơn giản hơn là git rebase <SHA1 of B> topic. Điều này làm việc bất kể bạn HEADđang ở đâu .

Chúng tôi có thể xác nhận hành vi này từ git rebase doc

<upstream>Chi nhánh thượng nguồn để so sánh với. Có thể là bất kỳ cam kết hợp lệ , không chỉ là một tên chi nhánh hiện có. Mặc định cho dòng ngược được cấu hình cho nhánh hiện tại.


Bạn có thể nghĩ điều gì sẽ xảy ra nếu tôi đề cập đến SHA1 topictrong lệnh trên?

git rebase <SHA1 of B> <SHA1 of topic>

Điều này cũng sẽ hoạt động nhưng rebase sau đó sẽ không Topicchỉ ra chi nhánh mới được tạo ra và HEADsẽ ở trạng thái tách rời. Vì vậy, từ đây bạn phải xóa thủ công cũ Topicvà tạo một tham chiếu nhánh mới trên nhánh mới được tạo bởi rebase.


3

Tôi đã sử dụng hỗn hợp các giải pháp được mô tả ở trên:

$ git branch temp <specific sha1>
$ git rebase --onto temp master topic
$ git branch -d temp

Tôi thấy nó dễ đọc và dễ hiểu hơn nhiều. Giải pháp được chấp nhận dẫn tôi đến một cuộc xung đột hợp nhất (quá lười để khắc phục bằng tay):

$ git rebase temp
First, rewinding head to replay your work on top of it...
Applying: <git comment>
Using index info to reconstruct a base tree...
M       pom.xml
.git/rebase-apply/patch:10: trailing whitespace.
    <some code>
.git/rebase-apply/patch:17: trailing whitespace.
        <some other code>
warning: 2 lines add whitespace errors.
Falling back to patching base and 3-way merge...
Auto-merging pom.xml
CONFLICT (content): Merge conflict in pom.xml
error: Failed to merge in the changes.
Patch failed at 0001 <git comment>
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

1
tương tự ở đây, một số tệp đã có xung đột khi tôi sử dụng 2 câu trả lời phổ biến nhất (ví dụ: r0hitsharma và Dymitruk)
Oliver

3

Vì việc nổi loạn là rất cơ bản, đây là một bản mở rộng câu trả lời của Nestor Milyaev . Kết hợp ý kiến của jszSimon South từ câu trả lời của Adam Dymitruk mang lại lệnh này hoạt động trên topicnhánh bất kể nó có phân nhánh từ mastercam kết của chi nhánh Ahay C:

git checkout topic
git rebase --onto <commit-B> <pre-rebase-A-or-post-rebase-C-or-base-branch-name>

Lưu ý rằng đối số cuối cùng là bắt buộc (nếu không, nó sẽ tua lại chi nhánh của bạn để cam kết B).

Ví dụ:

# if topic branches from master commit A:
git checkout topic
git rebase --onto <commit-B> <commit-A>
# if topic branches from master commit C:
git checkout topic
git rebase --onto <commit-B> <commit-C>
# regardless of whether topic branches from master commit A or C:
git checkout topic
git rebase --onto <commit-B> master

Vì vậy, lệnh cuối cùng là lệnh mà tôi thường sử dụng.


-2

Có một cách khác để làm điều đó hoặc nếu bạn muốn quay trở lại nhiều hơn chỉ một cam kết.

Dưới đây là một ví dụ để quay lại nsố lần xác nhận:

git branch topic master~n

Vì lợi ích của câu hỏi này, điều này cũng có thể được thực hiện:

git branch topic master~1

Lệnh hoạt động hoàn hảo trên git version 2.7.4. Không được thử nghiệm trên bất kỳ phiên bản nào khác.


Bạn đã giải thích sai điều này như là một câu hỏi về phân nhánh? Đây thực sự là một câu hỏi về việc nổi loạn.
NetherGranite
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.