ysap
ysap

Reputation: 8115

What is wrong with this Git rebasing attempt?

I am trying to learn how to work with Git "by the book". Working on a project with multiple developers, we share the same "origin" remote. I am developing my code in a working area which is apart from the local git repo.

Let's call my branch br_ysap. I made changes to my code and am trying to introduce these into my branch, and ultimately to the master branch. The remote master was updated since the last time I synced stuff up. So, I want to rebase my branch to teh current master, then apply my updates, commit and push.

Here's what I am doing and the response:

> git clone ...

> git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

> git checkout br_ysap
Branch br_ysap set up to track remote branch br_ysap from origin.
Switched to a new branch 'br_ysap'

> git rebase master
First, rewinding head to replay your work on top of it...
Applying: SOME-COMMIT-COMMENT
Using index info to reconstruct a base tree...
M       Main/Init.c
M       Main/Main.c
A       Service/Service_1.c
A       Service/Service_2.c
M       build_options.mk
Falling back to patching base and 3-way merge...
Auto-merging build_options.mk
Auto-merging Service/Service_3.c
CONFLICT (content): Merge conflict in Service/Service_3.c
Auto-merging Main/Main.c
CONFLICT (content): Merge conflict in Main/Main.c
Auto-merging Main/Init.c
CONFLICT (content): Merge conflict in Main/Init.c
Failed to merge in the changes.
Patch failed at 0001 SOME-COMMIT-COMMENT
The copy of the patch that failed is found in:
   c:/local_repo/.git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

Sometime in the past, Service_2.c was renamed to Service_3.c.

Now, I get "conflict" messages on Main.c, Init.c and Service_3.c. Next thing, I am resolving the conflicts. Checking the current status:

> git status
rebase in progress; onto fcd827a
You are currently rebasing branch 'br_ysap' on 'fcd827a'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   build_options.mk

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

        both modified:   Main/Init.c
        both modified:   Main/Main.c
        both modified:   Service/Service_3.c

So, I now copy the 3 files from my working space onto the git directory (that's instead of using a merge tool and to introduce new updates into the code).

> cp ../dev-tree/Main/Init.c         Main/Init.c
> cp ../dev-tree/Main/Main.c         Main/Main.c
> cp ../dev-tree/Service/Service_3.c Service/Service_3.c

Following this tutorial, I then stage the files:

> git add Main/Init.c Main/Main.c Service/Service_3.c

> git status
rebase in progress; onto fcd827a
You are currently rebasing branch 'br_ysap' on 'fcd827a'.
  (all conflicts fixed: run "git rebase --continue")

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   Main/Init.c
        modified:   Service/Service_3.c
        modified:   build_options.mk

Question 1: what happened to Main/Main.c? Why is it gone?

Next, following the instructions, I continue the rebasing:

> git rebase --continue
Applying: SOME-COMMIT-COMMENT
Applying: SOME-COMMIT-COMMENT - review update
Using index info to reconstruct a base tree...
M       Main/Init.c
Falling back to patching base and 3-way merge...
Auto-merging Main/Init.c
CONFLICT (content): Merge conflict in Main/Init.c
Failed to merge in the changes.
Patch failed at 0002 SOME-COMMIT-COMMENT - review update
The copy of the patch that failed is found in:
   c:/local_repo/.git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

Question 2: Why is Main/Init.c still conflicting? I have overwritten the file with my newer version?

Checking status, copying file, adding and checking status:

> git status
rebase in progress; onto fcd827a
You are currently rebasing branch 'br_ysap' on 'fcd827a'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

        both modified:   Main/Init.c

no changes added to commit (use "git add" and/or "git commit -a")

> cp ../dev-tree/Main/Init.c Main/Init.c

> git add Main/Init.c

> git status
rebase in progress; onto fcd827a
You are currently rebasing branch 'br_ysap' on 'fcd827a'.
  (all conflicts fixed: run "git rebase --continue")

nothing to commit, working directory clean

Supposedly ready to finish the rebasing, no? Well:

> git rebase --continue
Applying: SOME-COMMIT-COMMENT - review update
No changes - did you forget to use 'git add'?
If there is nothing left to stage, chances are that something else
already introduced the same changes; you might want to skip this patch.

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

> git status
rebase in progress; onto fcd827a
You are currently rebasing branch 'br_ysap' on 'fcd827a'.
  (all conflicts fixed: run "git rebase --continue")

nothing to commit, working directory clean

Question 3: What gives??? No, I did not forget the git add, thank you.

When I try to commit and push the branch, nothing is done:

> git push origin br_ysap:br_ysap
Everything up-to-date

Question 4: What is wrong with this rebasing process?


Update 1: Following @torek's description, suggesting I am missing the final commit step, I tried to do a rebase --skip. Unfortunately, the result was unexpected and over whelming:

> git rebase --skip
Applying: ANOTHER_COMMENT
Using index info to reconstruct a base tree...
A       Application/App_3.c
M       CSP/Driver_1.h
M       CSP/Driver_1.c
A       CSP/Driver_2.h
M       CSP/Chip.h
M       Common/Con_1.c
M       Diags/Con_2.c
M       Diags/Makefile
M       Main/Init.c
M       Main/Main.c
M       Main/Makefile
M       OS/Makefile
A       Service/Service_4.h
A       Service/Service_5.h
A       Service/Service_2.c
<stdin>:592: trailing whitespace.
//   $Id$
<stdin>:122074: trailing whitespace.
#define SOME_MOACRO_1
<stdin>:123643: trailing whitespace.
#define SOME_MOACRO_2
<stdin>:128989: trailing whitespace.
#define SOME_MOACRO_3
<stdin>:134620: trailing whitespace.
#define SOME_MOACRO_4
warning: squelched 9 whitespace errors
warning: 14 lines add whitespace errors.
Falling back to patching base and 3-way merge...
Auto-merging Service/Service_3.c
CONFLICT (content): Merge conflict in Service/Service_3.c
Auto-merging OS/Makefile
CONFLICT (content): Merge conflict in OS/Makefile
Auto-merging Main/Makefile
CONFLICT (content): Merge conflict in Main/Makefile
Auto-merging Main/Main.c
CONFLICT (content): Merge conflict in Main/Main.c
Auto-merging Main/Init.c
CONFLICT (content): Merge conflict in Main/Init.c
Auto-merging Diags/Makefile
CONFLICT (content): Merge conflict in Diags/Makefile
Auto-merging Diags/Con_2.c
CONFLICT (content): Merge conflict in Diags/Con_2.c
Auto-merging Common/Con_1.c
CONFLICT (content): Merge conflict in Common/Con_1.c
CONFLICT (modify/delete): Application/App_3.c deleted in HEAD and modified in ANOTHER_COMMENT Version ANOTHER_COMMENT of Application/App_3.c left in tree.
Failed to merge in the changes.
Patch failed at 0003 ANOTHER_COMMENT
The copy of the patch that failed is found in:
   c:/local_repo/.git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

Update 2: Before doing the --skip I copied the directory so I can experiment from the point before Update 1. So, changing to that copy, I tried to commit --allow-empty instead of the --skip. I keep finding myself in what seems as a dead-end:

> git commit --allow-empty -m "YET-MORE-COMMENT"
[detached HEAD 4dd9e38] YET-MORE-COMMENT

> git status
rebase in progress; onto fcd827a
You are currently rebasing branch 'br_ysap' on 'fcd827a'.
  (all conflicts fixed: run "git rebase --continue")

nothing to commit, working directory clean

It looks like the branch was detached finally, but was not attached to `

Upvotes: 1

Views: 1444

Answers (1)

torek
torek

Reputation: 488013

In Q1, what happened is that the Main/Init.c you copied in:

cp ../dev-tree/Main/Init.c Main/Init.c

(and then git added) matches the version in the tree you're rebasing onto.

Remember that git rebase works by getting onto an anonymous branch that grows from the --onto commit—this defaults to the branch tip you're rebasing to, in this case the tip of master—and then repeating a git cherry-pick of each commit in the set-of-commits-to-copy. (The commits-to-copy are those listed by git log master..br_ysap, in this case.)

Your current commit (middle, or really start, of rebase) is a commit on the anonymous branch that git rebase is building. I'll give the branch a name to make it easier to see. Things look like this (I'm assuming a very short set of commits between the original base and the rebase, so the master section is probably too short, but the idea should be clear enough):

          A - B - C - ... - S  <-- br_ysap
        /
... - o - o - o - T   <-- master, rebase-temp

Here commit T ( the Tip of branch master), is what you're rebasing on-to. The temporary branch also points to commit T. The commit git is currently trying to copy (and failing) is commit A, which, when rebase tells git to cherry-pick it, says to change something in Main/Init.c, but git couldn't make that change.

(If you take the commit ID that's being applied when things hit a conflict—the ID of A here)—you can run git show <commit-id> to see the entire commit, or git show <commit-id> -- Main/Init.c to see what happened to the one file. Git was unable to apply those changes to the tip of the anonymous branch, so it gave you a conflict error.)

You then replaced the work-tree file (which had the conflict markers and everything) with your ../dev-tree file. After you "git add"ed everything, you ran git status. This compares the index—what you're about to commit—with the contents of the most recent commit, which is commit T. If a file matches the file in T, git status says nothing.

At this point you ran git rebase --continue. This makes a new commit, which git is proud to claim is Just As Good As Commit A1 so we call it A':

          A - B - C - ... - S  <-- br_ysap
        /
... - o - o - o - T   <-- master
                   \
                    A'   <-- rebase-temp

Git now continues on with git cherry-picking commit B, which works fine, no conflicts—you can see why if you git show the original B and look at what would be in rebase-temp at this point. In any case now we have this:

          A - B - C - ... - S  <-- br_ysap
        /
... - o - o - o - T   <-- master
                   \
                    A' - B'   <-- rebase-temp

Now git tries to copy commit C. If you git show this commit, you'll see that the cherry-pick is supposed to make some more new changes to Main/Init.c, but again, git can't make those changes: they don't fit.

So, git stops with a conflict. You're supposed to now edit the file and fix it up, but presumably you can just copy and add your ../dev-tree version again (which you do, so that's Q2 and Q3 taken care of). Now you run git rebase --continue and you get your final complaint from git (which relates to Q4):

Applying: SOME-COMMIT-COMMENT - review update
No changes - did you forget to use 'git add'?

Take a close look at commit C again, as shown by git show. I suspect it has changes only to Main/Init.c.2 Those changes don't do anything to commit B' because that one already has them in it: they're the final changes to make the version of Main/Init.c in commit B match the version of Main/Init.c in commit T (tip of master).

What this means is that all the changes you want are already in. You can force a "no changes" commit with git commit --allow-empty if you want to keep the empty commit, but git rebase won't do that for you. Rebase prefers for you to say git rebase --skip-commit to tell it that, gosh, nothing from commit C is required after all. It will then move on to the remaining commits—who knows how many there are (you can find out!)—and, if all goes well throughout those, do its finishing-up job, which consists of moving the branch br_ysap so that it points to the tip-most commit it did make, which I'll assume here is S':

          A - B - C - ... - S  [abandoned]
        /
... - o - o - o - T   <-- master
                   \
                    A' - B' - D' - ... - S'  <-- br_ysap

You did not do that "skip" step so the rebase is still in progress, and your branch label br_ysap still points to original commit C, not to copied commit B'. Hence git push origin br_ysap finds nothing to push.


1Whether it is "just as good" depends on how good a job you did when you resolved the merge conflict.

2Another possibility is that it has changes to some other file(s) that match changes that git is able to detect are already present, vs the "merge base" at which the old br_ysap and master join. I believe you would have seen the "Falling back to ... 3-way merge" message in this case, though.

Upvotes: 4

Related Questions