Reputation: 8595
I am doing a git bisect
and after arriving to the problematic commit, I am now trying to get a step forward/backward to make sure I am in the right one.
I know of HEAD^
to go backwards in history but is there another shortcut to get me forward (towards a specific commit in the future) like so:
A - B - C(HEAD) - D - E - F
I know that my target is F and I want to move from C to D.
NOTE: this is not a duplicate of Git: How to move back and forth between commits, my question is slightly different and is not answered there
Upvotes: 162
Views: 176900
Reputation: 97
Here are a few self-contained functions you can put in your .bashrc file to help you navigate between commits. This set of functions should work for all git projects. Forward and Backward navigation is also covered in the following block of code.
PS. It works only for Linux
function git_next_commit(){
CURR_COMMIT=$(git rev-parse HEAD)
git checkout $(git log --branches -1 --pretty=format:"%H")
NEXT_COMMIT=$(git rev-list "$CURR_COMMIT"..HEAD | tail -1)
git checkout "$NEXT_COMMIT"
}
function git_prev_commit(){
git checkout HEAD^1
}
function git_first_commit(){
git checkout $(git rev-list HEAD | tail -1)
}
function git_latest_commit(){
git checkout $(git log --branches -1 --pretty=format:"%H")
}
The following is an explanation to each function:
git_next_commit
git_prev_commit
git_first_commit
git_latest_commit
Upvotes: 1
Reputation: 31
This is what I do, and it works fine most of the time. But if it cannot find the branch name, it will try to use the branch name from remote origin, which is a little slower and needs internet connection.
NOTE: I haven't tried to handle all errors.
gn() {
# Check if we're in a git repository
if ! git rev-parse --is-inside-work-tree > /dev/null 2>&1; then
echo "Not in a git repository"
return 1
fi
# Check if there are any commits in the current branch
if ! git rev-parse HEAD > /dev/null 2>&1; then
echo "No commits in the current branch"
return 1
fi
# Try to get the name of the remote branch that the HEAD points to
local branch=""
if git rev-parse refs/remotes/origin/HEAD > /dev/null 2>&1; then
branch=$(git branch -r --points-at refs/remotes/origin/HEAD | grep '\->' | cut -d' ' -f5 | cut -d/ -f2)
fi
if [ -z "$branch" ]; then
# Fallback: Extract branch name another method
branch=$(basename $(git rev-parse --show-toplevel)/.git/refs/heads/*)
fi
# If there's still no branch or an error, perform another fallback action
if [ -z "$branch" ]; then
local fallback_branch=$(git remote show origin | sed -n '/HEAD branch/s/.*: //p')
local next_commit=$(git rev-list --topo-order HEAD..$fallback_branch | tail -1)
git checkout $next_commit
return
fi
# Get the hash of the next commit
local next_commit=$(git log --reverse --pretty=%H $branch | grep -A 1 $(git rev-parse HEAD) | tail -n1)
# If there's no next commit, we're already at the last commit
if [ -z "$next_commit" ]; then
echo "Already at the last commit"
return 1
fi
# Try to checkout the next commit, and use the fallback command in case of an error
git checkout $next_commit
}
gp() {
if ! git rev-parse --is-inside-work-tree > /dev/null 2>&1; then
echo "Not in a git repository"
return 1
fi
# Check if there are any commits in the current branch
if ! git rev-parse HEAD > /dev/null 2>&1; then
echo "No commits in the current branch"
return 1
fi
git checkout HEAD^1
}
Upvotes: 0
Reputation: 11581
Traversing backward is trivial since you are moving down the tree, and there's always one way to go
function git_down
git checkout HEAD^
end
When traversing forward you are moving up the tree, so you need to be explicit which branch you are targeting:
function git_up
git log --reverse --pretty=%H $argv | grep -A 1 (git rev-parse HEAD) | tail -n1 | xargs git checkout
end
Usage: git_down
, git_up <branch-name>
Upvotes: 8
Reputation: 1
I had the similar issue when I had to move to F I did git checkout branch_name
which will move the HEAD to the latest commit in that particular branch
When I used git reset HEAD{0}
all the changes I have made after the current commit (here it would be C) were changed to modified, which was not what I wanted.
Upvotes: 0
Reputation: 771
I'm joining this comments a little bit later but I built this line of code that helped me go "forward" on any git tree
and I hope you can find it useful as well.
Note that I am not a bash magician so if you find something that could be changed, comment and we can make this line of code a better one!
To go backward:
git log --all --decorate --oneline | grep -A 1 $(git rev-parse --short HEAD) | awk '{print $1}' | tail -1 | xargs -I {} git checkout {}
To go forward:
git log --all --decorate --oneline | grep -B 1 $(git rev-parse --short HEAD) | awk '{print $1}' | head -1 | xargs -I {} git checkout {}
Here is some explanation:
git reflog # Here we get all the commits we need
grep -A 1 $(git rev-parse --short HEAD) # Here we grep for the short commit hash
awk '{print $1}' # Here we get just the hash
tail -1 # We will get at least 2 results, pick the last line, the first would be 0
xargs -I {} git checkout {} # Here we go "forward by 1"
Hope this can be helpful to anyone that would like to get this same behaviour. I'm building a CLI which has a command that moves you "back and forth" in the commits tree without prior knowledge of hashes or logs.
Upvotes: 2
Reputation: 290
I would use git-reflog
and git-reset
.
It is not the same case as you run git-bisect
, but suppose you git-reset
to commit C and want to move it back to commit F.
At the point, git-reflog
looks like this:
$ git reflog show
4444444 (HEAD -> main) HEAD@{0}: reset: moving to 4444444
1111111 HEAD@{1}: commit: F
2222222 HEAD@{2}: commit: E
3333333 HEAD@{3}: commit: D
4444444 (HEAD -> main) HEAD@{4}: commit: C
5555555 HEAD@{5}: commit: B
6666666 HEAD@{6}: commit: A
Then, you can run git-reset
to go back to any commit by specifying SHA1 hash or offset number from HEAD.
In your case, run git-reset
as follows:
$ git reset 1111111
or
$ git reset HEAD@{1}
Upvotes: 0
Reputation: 9678
To GUI and vscode users I would recommend using the extension git graph
Upvotes: 0
Reputation: 12677
If you want to see ahead, you can do this trick, as Git doesn't have strict command for it.
git log --reverse COMMIT_HASH..
List of log history hashes:
A
B
C -> put this
D
using command git log --reverse C..
, in output you will see B and A.
Upvotes: 4
Reputation: 1481
I believe you can do:
git reset HEAD@{1}
To go one commit forward in time. To go forward multiple commits, use HEAD@{2}, HEAD@{3}, etc.
Upvotes: 105
Reputation: 41268
I've experimented a bit and this seems to do the trick to navigate forwards (edit: it works well only when you have a linear history without merge commits):
git checkout $(git rev-list --topo-order HEAD..towards | tail -1)
where towards
is a SHA1 of the commit or a tag.
Explanation:
$()
means: get all the commits between current HEAD
and towards
commit (excluding HEAD
), and sort them in the precedence order (like in git log
by default -- instead of the chronological order which is weirdly the default for rev-list
), and then take the last one (tail
), i.e. the one we want to go to. git checkout
to perform a checkout.You can define a function accessible as a parameter-expecting alias in your .profile
file to navigate forward towards the particular commit:
# 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~'
Upvotes: 83
Reputation: 1
branchName=master; commitInOrder=1; git checkout $(git log --pretty=%H "${branchName}" | tac | head -n "${commitInOrder}" | tail -n 1)
where:
branchName
equals branch name
commitInOrder
equals a commit in order from very first commit in the selected branch (so 1 is the very first commit, 2 is second commit in branch, etc.)
Upvotes: 0
Reputation: 3316
If you are using vs code then Git history is an awesome plugin where you can efficiently see commits and check their contents in the editor itself. check out the link
Upvotes: 0
Reputation: 9416
This is what I'm using to navigate back and forth.
moving to next commit
function n() {
git log --reverse --pretty=%H master | grep -A 1 $(git rev-parse HEAD) | tail -n1 | xargs git checkout
}
moving to previous commit
function p() {
git checkout HEAD^1
}
Upvotes: 35
Reputation: 151
I just did a test on this. say for example you are in master branch Then do:
git checkout HEAD@{3}
So head gets detached, and you can then try it again to go to any other commit:
git checkout HEAD@{4}
Once you are done looking around, you can go back to your original state just by checking out to that branch. In my example: master branch
git checkout master
If you don't want to go to original state, and want so keep one of the commits as your head and continue from there, then you need to branch out from there. for example after "git checkout HEAD@{4}" , you can issue
git checkout -b MyNewBranch
Upvotes: 2
Reputation: 2712
As a workaround, you can just return to HEAD with
git checkout <branch>
And then move to the commit you'd like to, with
git checkout HEAD~<offset>
Upvotes: 0
Reputation: 809
All you need to get clear, not detached head state is to reset, not checkout.
git reset HEAD@{1}
Upvotes: 58
Reputation: 67733
Say F is the latest commit on trunk
(insert your own branch name here) ... you can refer to it as trunk~0
(or just trunk
), E as trunk~1
, D as trunk~2
etc.
Take a look in your reflog for yet more ways to name commits.
Upvotes: 9
Reputation: 1527
Probably not the nicest way but you can use git log
to view the list of commits and then use git checkout [sha1 of D]
to move to D.
Upvotes: 0