When I try to get into the interactive rebase mode as explained here, all I'm getting is noop which is weird but I cannot figure out why.
I am inside a temp branch in my machine and when I try to view the difference between, I get this:
Tugberk@TUGBERKPC /D/Dropbox/AppsPublic/gulp-aspnet-k (temp-a) $ git log --pretty=oneline temp-a..ajryan/dev f0ef4ee40fde4de5360515fd556de9c3ed40431c update readme with new optionsfcba1ae3306ae041a1d82647e4cf65a0c96fe2f7 do not loop for build, restore8bedd238660908f06d698815ef12fce786d716ed fix command concat e2e3a0d00e3950911c00c0e6e51a671f5f2ac2d3 bump version d65923ff91eb9d6b9782bc2bf5ab606d19733203 add quiet option 91a1faff0a3aa1ca552df2151190a3ecd87cfc6f allow caller to loop all comma962c55854457314324d81457c42913532875cc85 trailing whitespace, tabs > sp
However, when I run the following command, I'm getting noop:
Tugberk@TUGBERKPC /D/Dropbox/AppsPublic/gulp-aspnet-k (temp-a) $ git rebase -i ajryan/dev
Any idea why and what am I doing wrong?
Your
git log
command is showing commits that are onajryan/dev
but not ontemp-a
. Yourrebase
command is told to rebase commits that are ontemp-a
but not onajryan/dev
, but presumably there are no such commits. Pictorially:Therefore, there are no commits to pick up and rebase onto the commit I have marked with a star
*
. Sorebase -i
generates a single no-op command.If you execute that no-op, the rebase operation should conclude by moving
temp-a
to point the last commit added on after*
, which should maketemp-a
point to commit*
. (If that doesn't happen, it's a small bug inrebase
.) If you delete the no-op, though, the rebase operation will be aborted, andtemp-a
will not be moved.Note that if you did have different items on
temp-a
you could rebase them, or rebase the other branch. Here are two more illustrations, starting with almost the same base, but with one commit that's ontemp-a
that is not onajryan/dev
:In this case, you can either put the
@
commit on top of the other commits:or at the bottom, below the other commits:
Here I have left out the labels (
temp-a
andajryan/dev
). This is on purpose: in git, branch labels are easy to move around (and are actually expected to move as long as it's in a "forward" fashion); all of the actual branch-iness is stored in the commits themselves. Whatgit rebase
really does is copy old commits to new ones, so that you can change their parent-age and/or contents.In Illustration #1, we've re-parented commit
@
: it now has commit*
as its parent. All the other commits are unchanged, and hence only commit@
has to be copied.In Illustration #2, however, we've re-parented the entire chain of commits that were originally only on
ajryan/dev
. Commit@
itself is unchanged, but we had to copy all four of the originalo - o - o - *
commits: we copy the firsto
to set its parent to@
. This new commit has a new and different SHA-1 ID, so we copy the secondo
to use the new commit's ID as its parent. This second copy forces the third copy, which then forces the last copy of the*
commit. See the next section as to what can be bad about that.In git, to join two different chains of commits, you would normally use
git merge
:Here merge commit
M
joins the two chainsA - B
andC - D
that fork off fromo
. If all ofA
throughD
exist, and you create new commitM
, nothing happens toA
throughD
: they are still the same as before.For simple development, though, it's usually nicer to "rebase" work: to make
C
andD
sit "on top" ofA - B
. But to do that you must copy the commits, because a commit in git is permanent and unchanging. You copyC
to a new, slightly differentC'
, and copyD
to a new, slightly differentD'
. The main thing that's different aboutC'
is that its parent isB
, noto
, and the main thing that's different aboutD'
is that its parent isC'
, notC
:Once the rebase copy is done, you simply abandon the old
C
andD
commits and start pretendingC'
andD'
were the original commits:Now we no longer need the little kink in the graph since we've forgotten all about the original
C
andD
, and history just looks like this:The dirty secret (not so secret, not that dirty) is that the original
C
andD
are still in there, they're just invisible (to you). The part that makes it truly dirty happens if someone else has the original twoC
andD
commits. Git commit IDs are cryptographic checksums of their contents, including the parent SHA-1 IDs, so the originalC
andD
are still there and are different from the newC
andD
, and anyone else.This matters when you pull commits from someone else: if you rebase their commits onto your work, you change the IDs. You make new copies and you might forget they had the originals, but they don't know about your new copies yet and they have not forgotten their original IDs.
If you later present them with rebased copies of their commits, they need to do more work to pick up your copies of their commits and maybe move any additional work they've done.
In short, unless you've made arrangements with them (whoever "they" may be) in advance, a rebase like that shown in Illustration #2 is usually not a good idea.
What about "fast forwards"? Well, let's take a look at Illustration #1 again. Let's assume the
o - o - o - @
chain came from someone else (a "pull request"), and that you add commit*
on top of it by rebasing your work onto theirs. Let's re-draw the graph too, adding one more kink, and put theajryan/dev
label back:Now let's further assume that they, whoever they are, have the
o - o - o - *
sequence on theirdev
branch and have not done any additional work, so that their stuff ends at the commit marked*
. If you make this commit-sequence available to them, they can pick it up and add commit@
to theirdev
branch, by simply sliding theirdev
label down-and-right to point to the@
label. Now let's also erase ourajryan/dev
label, and straighten out the kinks in these drawings, and use the labeldev
. Here's the result:This is what they will have, and what you will have, once you're both in sync. It's a very simple looking history, even if it's a slight deviation from the actual development process (you wrote commit
@
without first seeing their chain that ended in*
). But this is the kind of "rebase" that makes following the development work a lot easier, and is in general what people want to do.Sliding a label "rightward" (including down-and-right, or up-and-right, in drawings like these) is a "fast forward" operation. If you run
git merge
with--ff-only
, it will only do the "merge" if it is really this kind of non-merge fast-forward.Bottom line: I think you wanted to rebase your
@
commit onto theajryan/dev
work, but you didn't have an@
commit to rebase. That's why rebase gave you anoop
. In this case, a simple non-merge fast-forwardgit merge
suffices to make your label match their label.It's also worth thinking about this: when you do
git log X..Y
you're asking git to draw the graph like I did here:The
X..Y
notion essentially says: starting with this graph, take out a highlighter and highlight the commit thatX
points to, and all commits left-and/or-up-or-down from there. (That leaves the threeo
s pointed-to byY
un-marked.) Then, find all commits pointed-to byY
that are not already highlighted, and log those.If you reverse the
X
andY
, as ingit log Y..X
, you ask git to "run the highlighter" on commits marked byY
, which leaves only the twoo
s only onX
, and then log all commits pointed-to byX
that are not already highlighted. So here you get the twoo
s on X, and none of the shared ones.