I have seen so many posts and blogs about git rebase on feature branch, but still confused 100%.
Scenario: I did the following steps while working on a newly created feature branch from master.
% git clone <repository>
% git checkout -b feature_branch origin/master
% git add .
% git commit -m "initial commit"
% git push
% git feature_branch [-u|--set-upstream-to]
% git checkout master
% git pull
% git rebase
% git checkout feature_branch
//make some changes locally
% git add .
% git commit
% git push
At this stage I get message saying your remote branch is ahead of your local repository. Please pull first. When and how did the change into my remote feature branch occured?
I know here there is local repo of master and my feature branch and when I pull things they come from remote origin/master. So when I am making changes locally then my local feature_branch is ahead of remote feature_branch I assume.
Where could I be taking wrong steps in this context? What should be the actual steps for working on a initial local feature_branch which then goes to remote for backup in case something goes wrong while developing. I wanted to use rebase to keep the git history clean. Then what will be the steps? THe above as I mentioned?
I'm making several assumptions here; I'll note them.
Here is the sequence of commands, along with what I assume and what they then do:
I assume this works and you also then
cdinto the new clone (you didn't show this).This creates, in your new local repository, a new branch name,
feature_branch, that selects the same commit asorigin/master. The nameorigin/master—which is short forrefs/remotes/origin/master—is not actually a branch name, but rather, is your Git's memory of their (origin's)master, the last time your Git updated its own memory: in this case, at the time you rangit clone.They may or may not already have a branch named
feature_branchof their own! You created yourfeature_branchlocally. If they do have afeature_branch, yours is not related to theirs, unless by some chance theirs is also exactly the same as theirmaster(which is yourorigin/master).This wouldn't do anything, since you have not modified any files, and all the files in your working tree came out of the commit.
This also would not do anything. Your existing
feature_branchis still even with theirmaster, yourorigin/master.If you did modify some files before
git add .andgit commit, this made one new commit on yourfeature_branch.This would give you an error, in a modern Git, along with a lot of advice suggesting
git branch --set-upstream-toorgit push -uor something along those lines:This isn't a command. If this was actually:
and you got no errors here, that means they already had their own
feature_branch. This seems like the most likely issue here.(If you actually ran, instead:
that would create a new name,
feature_branch, over on the Git repository atorigin, based on yourfeature_branch, and we probably would not see any errors later.)There isn't really any point to doing these two, and they likely did nothing. If they did do something, it will generally be harmless—though this depends on who is doing what with the Git repository over on
origin.You are, at this point, still on your
master, and it would again do nothing.Here, we finally get somewhere: we add a new commit, locally, to the local branch named
feature_branch. But if they had their own feature branch, your local branch and theirs have now diverged, and:this would complain that you need to use
git pullor similar to accommodate their work.What you probably want to do at this point is:
which will copy your one actually-new-commit that you've made on your
feature_branch, that extends out from yourorigin/master. The new-and-improved copy of that one commit will go after their last commit on theirfeature_branch, which is yourorigin/feature_branch.As a drawing, we might have something that looks like this:
What this drawing means is:
Your current branch is
feature_branch. That's the special nameHEADhere.Your current commit is
K. The real hash ID of any commit is unique, but big and ugly and impossible for humans to deal with: it looks like random gibberish. So when we make simplified drawings of what's going on in a repository, we are often well-served by substituting in simpler names for the commits. So that's what I've done here.Their
master(yourorigin/master) and yourmasterboth select commitH.Their
feature_branch—yourorigin/feature_branch—selects some later thanHcommitJ.Note that I've drawn commits with later ones towards the right. Each commit links backwards to an earlier commit, so commit
Hlinks backwards to some earlier commitG, which links backwards to earlier commits I did not bother to try to draw. Meanwhile, commitJlinks backwards toI, which links backwards toH.This sort of backwards linkage is what Git commits are really all about. Every time you make a new commit, Git packages up a snapshot of every file, as of the form it has now (after
git add-ing a copy into Git: the copies you can see and work on/with aren't actually in Git, which has invisible secret copies). Git adds on metadata, such as your name and email address and the current date-and-time. Git makes this metadata such that it includes the hash ID of the current commit—in this case, commitK—and writes all of that out to produce a new commit, which we can callL:And now, as its last trick,
git commitwrites the raw hash ID of that commit into the current branch name, giving us:I don't have to draw
Lon a separate line here as I don't need to draw in any arrows pointing to commitK, but if you'd made another branch nametempthat pointed toK, I would leaveLon a separate line:This is (intended to be) the same drawing, but with the addition of the name
temp.(You might want to run
git log --all --graph --decorate --onelineor any of the other commands shown in the answers on Pretty Git branch graphs to view other ways of looking at the set of commits you have.)The point of
git rebaseis to copy commitsThe good thing about a commit is that it can never be changed... at all. You cannot change anything about an existing commit. Not even Git itself can do that.
The bad thing about a commit is that it can never be changed. Commit
K—the one you made on yourfeature_branchback when you started with:is stuck where it is, pointing backwards to existing commit
H. You might like everything else about it, but you don't like the fact that it points toH, because their latestfeature_branchcommit is commitJ. So you need a new and improved version ofK.The rebase command is about making these new and improved commits. It can copy old commit
Kto a new and improvedK'. It then has your Git repository abandon the oldKand use the new-and-improvedK'instead.The bad things about using rebase are:
You have to be careful about how many commits it copies. Sometimes the defaults are right, and sometimes they're not.
Because it copies commits, anyone else who has the originals *also has to abandon the old ones for the new-and-improved ones. This takes work on their part.
If you haven't given out the old ones, there's nobody else who has the old ones, so rebase is safe. Otherwise, you need to tell whoever has them that you are doing this.
Anyway, given:
running:
tells your Git: do copy
K, stop copying atH, and put the copies afterJ.If your commit graph is the one I drew, a simpler rebase would have the same effect—but your commit graph might instead look like this:
In this case, the simpler command (that I'm still not showing) would try to copy
Hto a new and improvedH', which you don't want to do.In both cases, after the rebase, your new and improved
K'will come afterJ, e.g.:Since we—and Git—find commits by starting from the branch names, following the arrows to the commits, and then working backwards (leftwards in these drawings), nobody will ever see commit
Kagain. It's still there—nobody and nothing can change it—but it might as well be gone.(Eventually—after a minimum of 30 days by default—Git will notice that nobody can find
K, and strip it away for real, but you won't notice that either.)