Làm cách nào để chuyển tới và lùi giữa các lần cam kết trong git?


104

Tôi đang thực hiện git bisectvà sau khi đến cam kết có vấn đề, tôi hiện đang cố gắng tiến / lùi một bước để đảm bảo rằng tôi đang ở đúng.

Tôi biết HEAD^phải quay ngược lại lịch sử nhưng có một con đường tắt nào khác để đưa tôi về phía trước (hướng tới một cam kết cụ thể trong tương lai) như vậy:

A - B - C(HEAD) - D - E - F

Tôi biết rằng mục tiêu của tôi là F và tôi muốn di chuyển từ C đến D .


LƯU Ý: đây không phải là bản sao của Git: Cách di chuyển qua lại giữa các lần cam kết , câu hỏi của tôi hơi khác một chút và không được trả lời ở đó


1
stackoverflow.com/questions/2263674/… cũng có thể giúp bạn.
VonC

git checkout -b new_branch HEAD~4quay trở lại 4 cam kết từ đầu như trong stackoverflow.com/a/4940090/911945
Anton Tarasenko

Câu trả lời:


57

Tôi đã thử nghiệm một chút và điều này dường như thực hiện mẹo để điều hướng về phía trước ( chỉnh sửa : nó chỉ hoạt động tốt khi bạn có lịch sử tuyến tính mà không có cam kết hợp nhất):

git checkout $(git rev-list --topo-order HEAD..towards | tail -1)

đâu towardslà SHA1 của cam kết hoặc thẻ.

Giải trình:

  • lệnh bên trong $()có nghĩa là: lấy tất cả các cam kết giữa hiện tại HEADtowardscam kết (không bao gồm HEAD) và sắp xếp chúng theo thứ tự ưu tiên (giống như git logtheo mặc định - thay vì thứ tự thời gian mà kỳ lạ là mặc định rev-list), rồi lấy thứ tự cuối cùng ( tail), tức là một trong những chúng tôi muốn đi đến.
  • điều này được đánh giá trong vỏ con và được chuyển đến git checkoutđể thực hiện thanh toán.

Bạn có thể xác định một hàm có thể truy cập được dưới dạng bí danh mong đợi tham số trong .profiletệp của mình để điều hướng về phía trước đối với cam kết cụ thể:

# Go forward in Git commit hierarchy, towards particular commit 
# Usage:
#  gofwd v1.2.7
# Does nothing when the parameter is not specified.
gofwd() {
  git checkout $(git rev-list --topo-order HEAD.."$*" | tail -1)
}

# Go back in Git commit hierarchy
# Usage: 
#  goback
alias goback='git checkout HEAD~'

2
Tiến tới hoạt động tốt trên các phần thẳng của lịch sử nhưng đi vào vòng lặp khi gặp phải sự hợp nhất.
Kostas

Vâng, tôi thực sự chưa thử nghiệm nó về hợp nhất. Tôi sẽ cố gắng để có một cái nhìn trong thời gian rảnh rỗi, nhưng tôi có ít động cơ tạm thời, vì chúng ta đã đồng ý để có một lịch sử nghiêm chỉnh tuyến tính trong dự án của chúng tôi;)
jakub.g

2
Câu trả lời chính xác! Sửa đổi để tự động xác định chi nhánh hiện tại: stackoverflow.com/a/23172256/480608
Raine Revere

Điều này là không đúng. git loghiển thị các cam kết theo thứ tự thời gian, theo mặc định, giống như rev-list, ngoại trừ khi sử dụng --graphcờ.
papiro

Bằng chứng khá thuyết phục rằng git quá phức tạp. Đối với những thứ thường đơn giản như Hoàn tác hoặc Làm lại, ở đây chúng tôi có một danh sách các câu trả lời mâu thuẫn nhau. Và tôi thậm chí còn chưa đi sâu vào bất kỳ câu trả lời nào được liên kết. FWIW, tôi đã thử một phiên bản của điều này với một tập hợp tuyến tính đơn giản của các cam kết và cuối cùng đã bỏ cuộc.
Snowcrash

49

Tất cả những gì bạn cần để có được trạng thái đầu rõ ràng, không tách rời là đặt lại chứ không phải thanh toán.

git reset HEAD@{1}

5
hoặc git reset "HEAD@{1}"trong một số shell nhất định như fish và powershell .. git reflogcũng có thể hữu ích để tìm cam kết chính xác.
steve cook

1
Luôn sử dụng dấu nháy đơn trong các lệnh shell trừ khi bạn muốn shell cố gắng diễn giải / mở rộng nội dung một cách rõ ràng. Điều này đặc biệt quan trọng trong những trường hợp như thế này, trong đó mục tiêu là ngăn trình bao thông dịch các ký tự đặc biệt. Bằng cách đó, bạn không cần biết liệu chuỗi có chứa bất kỳ điều gì có vấn đề hay không.
Chris Trang

Tôi đã làm điều đó để nhìn lại thời gian, sau đó tôi git reset HEADquay trở lại vị trí của tôi ... bây giờ tôi không biết kho lưu trữ của tôi đang ở trạng thái nào và mọi thứ thật đáng sợ. Tôi nên làm gì bây giờ?
theonlygusti

47

Tôi tin rằng bạn có thể làm được:

git reset HEAD@{1}

Để thực hiện một cam kết về phía trước trong thời gian. Để chuyển tiếp nhiều cam kết, hãy sử dụng HEAD @ {2}, HEAD @ {3}, v.v.


20

Đây là những gì tôi đang sử dụng để điều hướng qua lại.

chuyển sang cam kết tiếp theo

function n() {
    git log --reverse --pretty=%H master | grep -A 1 $(git rev-parse HEAD) | tail -n1 | xargs git checkout
}

chuyển sang cam kết trước đó

function p() {
    git checkout HEAD^1
}

Cảm ơn! Tôi đang sử dụng cái này ngay bây giờ! Lưu ý cho những người mới bắt đầu khác như tôi : để gắn lại HEAD, hãy git checkout <current branch>đính kèm bản cam kết mới nhất. git checkout -b <new-branch-name>từ cam kết hiện tại cho phép thay đổi trong nhánh mới. git rebase -icũng hoạt động. Ngoài ra , tôi đặt tên cho n()chức năng của mình là nx()để tránh xung đột với trình quản lý phiên bản nút "n". Đảm bảo kiểm tra bí danh!
Steven Choi

function () {...} là dành cho tác giả của một tập tin bash kịch bản Unix / Linux, tôi đến từ Windows, một ít khó khăn cho tôi để hiểu trước hết
IcyBrk

không hoạt động = (.
Madeo

9

Giả sử F là cam kết mới nhất trên trunk(điền tên chi nhánh của riêng bạn vào đây) ... bạn có thể gọi nó là trunk~0(hoặc chỉ trunk), E là trunk~1, D là trunk~2v.v.

Hãy xem trong bạn reflog cho chưa nhiều cách để cam kết tên.


1
~ Quay ngược lại, không foward, thân ~ 2 là Một
EmmanuelMess

Đúng. Câu trả lời này giả sử bạn có một nhánh được gọi là thân trỏ đến Fvà bạn biết vị trí trong lịch sử của nhánh đó mà bạn muốn chuyển đến. Nó không cố gắng tiến về phía trước so với HEAD, mà là ít hơn so với thân cây.
Vô dụng

@EmmanuelMess thế nào trunk~2A?
theonlygusti

@theonlygusti Bạn quay lại từ HEAD hai lần.
EmmanuelMess

Bạn vẫn giả định rằng nhánh trunkvà dòng điện của bạn HEADgiống hệt nhau, điều này không được hiển thị trong câu hỏi, điều mà tôi đã nêu không phải là những gì tôi đang giả định và điều này rất khó xảy ra ở nửa chặng đườngbisect
Vô dụng

3

Đi qua phía sau là việc nhỏ vì bạn đang di chuyển xuống cái cây và luôn có một cách để đi

  function git_down
        git checkout HEAD^
  end

Khi di chuyển về phía trước, bạn đang di chuyển lên trên cây, vì vậy bạn cần phải rõ ràng bạn đang nhắm mục tiêu vào nhánh nào:

  function git_up 
        git log --reverse --pretty=%H $argv | grep -A 1 (git rev-parse HEAD) | tail -n1 | xargs git checkout
  end

Cách sử dụng: git down,git up <branch-name>


Đi lùi cũng không hoàn toàn là duy nhất, khi có liên quan đến hợp nhất. Mặc dù HEAD^thường là một mặc định hợp lý.
kdb

2

Nếu bạn muốn nhìn thấy trước, bạn có thể thực hiện thủ thuật này, vì Git không có lệnh nghiêm ngặt cho nó.

git log --reverse COMMIT_HASH..

Thí dụ

Danh sách băm lịch sử nhật ký:

A
B
C -> put this
D

sử dụng lệnh git log --reverse C.., trong đầu ra bạn sẽ thấy BA .


1

Có lẽ không phải là cách đẹp nhất nhưng bạn có thể sử dụng git logđể xem danh sách các cam kết và sau đó sử dụng git checkout [sha1 of D]để chuyển đến D.


6
Tôi không hiểu, nếu anh ấy ở C, thì git log sẽ chỉ hiển thị cho anh ấy C, B và A.
Bilthon 27/11/12

Ok, hiểu rồi, nhưng bạn sẽ phải thực hiện một git-log phức tạp như được chỉ ra trong liên kết được cung cấp bởi VonC
Bilthon 27/11/12

1

Tôi vừa làm một bài kiểm tra về điều này. Ví dụ bạn đang ở trong nhánh chính Sau đó làm:

git checkout HEAD@{3}

Vì vậy, đầu được tách ra và sau đó bạn có thể thử lại để chuyển sang bất kỳ cam kết nào khác:

git checkout HEAD@{4}

Sau khi hoàn tất việc quan sát xung quanh, bạn có thể quay lại trạng thái ban đầu chỉ bằng cách kiểm tra chi nhánh đó. Trong ví dụ của tôi: chi nhánh chính

git checkout master

Nếu bạn không muốn chuyển sang trạng thái ban đầu và muốn giữ một trong những cam kết làm đầu và tiếp tục từ đó, thì bạn cần phải phân nhánh từ đó. ví dụ: sau "git checkout HEAD @ {4}", bạn có thể phát hành

git checkout -b MyNewBranch

0

Để giải quyết vấn đề, bạn chỉ có thể quay lại HEAD với

git checkout <branch>

Và sau đó chuyển sang cam kết mà bạn muốn, với

git checkout HEAD~<offset>

0

Nếu bạn đang sử dụng so với mã thì lịch sử Git là một plugin tuyệt vời, nơi bạn có thể xem các cam kết và kiểm tra nội dung của chúng trong chính trình chỉnh sửa một cách hiệu quả. kiểm tra liên kết


0
branchName=master; commitInOrder=1; git checkout $(git log --pretty=%H "${branchName}" | tac | head -n "${commitInOrder}" | tail -n 1)

Ở đâu:

branchName bằng tên chi nhánh

commitInOrder bằng một cam kết theo thứ tự từ cam kết đầu tiên trong nhánh đã chọn (vì vậy 1 là cam kết đầu tiên, 2 là cam kết thứ hai trong nhánh, v.v.)

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.