103

Here's an example:

>git status # On branch master nothing to commit (working directory clean) >git checkout -b test-branch >vi test.c >git add test.c >git commit -m "modified test.c" >vi README >git add README >git commit -m "modified README" 

Now I want to do a 'git rebase -i' that will let me rebase all commits for this branch. Is there something like 'git rebase -i HEAD~MASTER' or similar. I figure I could do 'git rebase -i HEAD~2', but I really don't want to have to count how many commits have been made. I could also do 'git rebase -i sha1' but I don't want to comb through git log to find the first commit sha1. Any ideas?

2
  • 1
    Please title your question a little better. Perhaps mention you want to do an interactive rebase for all changes in a branch. Preferably in the form of a question (though not always possible). Commented Dec 12, 2008 at 20:58
  • Do you want to rebase onto a modified master or just edit the commits you just made on test-branch? Commented Nov 10, 2017 at 18:12

10 Answers 10

106

Ok, I'm asuming the branch is called "feature" and it was branched from "master".

There's this little git command called merge-base. It takes two commits and gives you the first common ancestor of both of those. So...

git merge-base feature master 

...will give you the first common ancestor of those two commits. Guess what happens when you pass that commit to git rebase -i, like...

git rebase -i `git merge-base feature master` 

Interactive rebase from the first common ancestor of both master and feature branch. Profit! ;)

Sign up to request clarification or add additional context in comments.

5 Comments

It's ugly though - is there no syntactic sugar on hand?
That's pretty compared to a lot of git solutions :).
I would suggestion to use git merge-base master HEAD which should always work for the current branch without typing out the current branch name. Alias this command and you've got your nice short git command.
git rebase -i $(git merge-base @{u} HEAD) -- that's assuming that your current branch is set to track the base branch. Example: git branch feature1 origin/master would track origin/master. So now you don't even have to type that.
@{u} doesn't work when pushing the new branch with -u (which is needed to able to pull it again, but changes the upstream) Example: Create a branch and switch to it git branch feature1 origin/master && git switch feature1. The current Upstream is [origin/master]. Push the branch git push -u. The current Upstream is [origin/feature1].
78

The issue with all provided solutions is, they do not allow you to rebase from the very first commit. If the first commit hash is XYZ and you do:

git rebase -i XYZ 

You only rebase starting from the 2nd commit.

If you want to rebase from the first commit you do:

git rebase -i --root 

2 Comments

But "--root" rebases from the first commit ever rather than the first commit in the branch which is what the OP is asking for
TIL --root. Just what I was looking for.
57

Have you tried: git rebase -i master?

4 Comments

This fails if the master is ahead of the current merge base in your branch.
The problem with git rebase -i master is that you may have merge conflicts that you don't necessarily want to deal with at the moment, or you may fix a conflict in one commit, only to fix it again in another commit during the course of the rebase. I've added an answer that provides an alternative to this, and an alternative to specifying the exact commit, or number of commits back that you want to rebase from.
rerere is your friend whenever you're rebasing.
Doesn't work for me, it results in weird merge conflicts after me doing… nothing, literally! I mean, all I did git rebase master (no -i just for tests), and it already results in conflicts.
22

Use gitk (*nix), or gitx (OS X) or similar on other platforms, and have a look at which commit was the root of your branch. Then run:

git rebase -i <the SHA hash of the root commit> 

For example, I have a repository that I inspected using gitx:

gitx screencap

Now that I know the root hash I can run this:

git rebase -i 38965ed29d89a4136e47b688ca10b522b6bc335f 

And my editor pops up with this and I can rearrange/squash/whatever as I please.

pick 50b2cff File 1 changes. pick 345df08 File 2 changes. pick 9894931 File 3 changes. pick 9a62b92 File 4 changes. pick 640b1f8 File 5 changes. pick 1c437f7 File 6 changes. pick b014597 File 7 changes. pick b1f52bc File 8 changes. pick 40ae0fc File 9 changes. # Rebase 38965ed..40ae0fc onto 38965ed # # Commands: # pick = use commit # edit = use commit, but stop for amending # squash = use commit, but meld into previous commit # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. # 

I'm sure there's some magic way to convince git to figure out the root of the tree automatically, but I don't know what it is.

EDIT: That magic is this:

git log master..other_feature | cat 

Which will show you all the commits on that branch, and piping to cat will disable the pager so you see the first commit immediately.

EDIT: combining the above gives a fully automated solution:

git rebase -i `git log master..other_feature --pretty=format:"%h" | tail -n 1`~ 

3 Comments

| cat seems like a useless use of cat when you can use git --no-pager.
@MatthieuMoy --no-pager probably wasn't in the version I was using in December 2008. That option hadn't landed in the got codebase until July 2008. github.com/git/git/commit/…
@Otto Ah, I didn't notice the date of your post. You meant to point to github.com/git/git/commit/… actually.
10

The OP's problem as I understand it

This answer applies if you don't actually want to rebase on top of master, but you'd rather just squash all commits into one, since diverging from master.

The problem with rebasing from a different branch

The problem with git rebase -i master is that you may have merge conflicts that you don't necessarily want to deal with at the moment, or you may fix a conflict in one commit, only to fix it again in another commit during the course of the rebase.

The problem with rebasing from a known commit

The whole problem here is that you have to know which commit you have to refer to, either by its SHA, or HEAD~x, etc. This is only a minor annoyance but it is an annoyance.

The better way

If you instead want to rebase all the commits in your current branch, since the most recent commit it shared with its parent branch, you can add the following alias to .gitconfig:

rbi = !sh -c \"git rebase -i `git merge-base $1 HEAD`\" - 

Usage

git rbi parentBranch 

You can then use an interactive rebase to pick/reword/edit/squash/etc all commits in your branch, since it diverged from the parent branch. My typical flow is to pick the oldest commit (the one at the top), and set all other commits to f (or fixup).

How it works

This alias is just a shell script, that is using an argument which refers to the parent branch. That argument is passed into git merge-base in order to determine the most recent shared commit between that branch, and the current branch.

4 Comments

I'm not sure this answer makes much sense at all with the given topology. The merge base here will be master at the time test-branch was created (say 3hgn45). So this rebase actually doesn't do anything. It says rebase this branch from (but not including) 3hgn45 to (and including) HEAD onto 3hgn45. But maybe I'm misunderstanding your suggestion...
EDIT: I get it; the OP's question just wasn't clear to me. You might consider adding a warning that this won't maintain changes from master (i.e. it's not really rebasing, since you're maintaining the same base, just editing some commits on the current branch).
@DylanYoung - yes, my understanding of the OP's question was that they didn't actually want to rebase on top of master, but just squash all commits into one, since diverging from master.
Here is a similar "rebase common ancestor" alias that allows additional arguments: rbca = "!git rebase $(git merge-base HEAD \"$1\") ${@:2} #"
6

Since Git v1.7.10, you can just run git rebase without argument, and it will find the fork point and rebase your local changes on the upstream branch.

You need to have configured the upstream branch for this to work (i.e. git pull without argument should work).

For more details, see the docs for git rebase:

If is not specified, the upstream configured in branch..remote and branch..merge options will be used (see git-config[1] for details) and the --fork-point option is assumed. If you are currently not on any branch or if the current branch does not have a configured upstream, the rebase will abort.

1 Comment

This is the actual up-to-date answer :)
2

A general solution (if you don't know the name of the upstream branch) is:

git rebase -i @{upstream} 

Note that if your upstream (probably a tracking branch) has updated since you last rebased, you will pull in new commits from the upstream. If you don't want to pull in new commits, use

git rebase -i `git merge-base --all HEAD @{upstream}` 

but that is a bit of a mouthful.

1 Comment

I have seen suggestions that the symmetric difference HEAD...master (which yields the merge base as the negated third sha) can be used in git rebase, but I can't work out how.
1
git rebase -i --onto @{u}... @{u} 

Interactive rebase starting from the single merge point of HEAD and its upstream including all commits in HEAD that are not in its upstream.

In other words exactly what you want.

1 Comment

But if @{u} is configured, then you can use argumentless git rebase, see my answer.
0

its a bit long, but i have it my history and i can use it on any repo

git rebase -i HEAD~$(git rev-list --count HEAD ^$(basename $(git symbolic-ref refs/remotes/origin/HEAD))) 

Comments

0

Well for a start nobody should be working from master/main. That is for storing the code that is currently in Production!!

You should have a Production/Staging release branch that is used by your deployment engine to deploy to Production. Once successfully deployed, merge Production/Staging release branch to master/main

Allows you to be able to do HOTFIXes if/when required!

You should have a Testing/UAT release branch that is used by your deployment engine to deploy to Testing/UAT

Secondly you should refresh your feature branch from any changes made by others to the Testing/UAT release branch

You should create feature branches from that Testing/UAT release branch: eg feature/TestRelease

So to do this right, from CLI ie Window command or Bash

git checkout feature/TestRelease

(First time: git checkout -b feature/TestRelease origin/feature/TestRelease)

git pull 

from UI ie GitHub, BitBucket, Azure DevOps Create your new feature eg feature/JIRA-875-LearnGIT based upon feature/TestRelease

back to CLI, make use you are on feature/TestRelease

git fetch 

this will display: feature/JIRA-875-LearnGIT -> origin/feature/JIRA-875-LearnGIT

Copy it type and paste to get: (remove the ->)

git checkout -b feature/JIRA-875-LearnGIT origin/feature/JIRA-875-LearnGIT 

it will change branch locally to feature/JIRA-875-LearnGIT

Do your stuff ie changes

then

git stash -m"Some description" git checkout - 

(This will get you back to previous local branch ie feature/TestRelease)

from feature/TestRelease

git pull git checkout - 

(This will get you back to previous local branch ie feature/JIRA-875-LearnGIT)

git rebase - 

(Rebase repoints your feature branch to the end of the release branch: ie feature/TestRelease)

fix any conflicts

then

git rebase --continue 

(Finishes rebase)

Then unstash your changes

git stash pop git add . && git commit --amend 

then push

git push origin HEAD -f 

Rebase repoints your feature branch to the end of the release branch

The golden rule of git rebase is to never use it on public branches; that is never on Release branches and master/main

Rebase your feature branch to your Release branch before Pushing your local changes to the git repro branch ie feature branch on Git server

Merge your feature branch to your Release branch, best done from UI ie GitHub, BitBucket, Azure DevOps

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.