4

Background:

  • I want to contribute a bug fix to a project that uses Git for version control;
  • I don't have write access to the project's public repository;
  • the project maintainer requires patches to be submitted via email.

Workflow:

  • I clone the project's public repository;
  • I create a local branch in which I develop my bug fix;
  • I email the patch to the maintainer;
  • the maintainer applies the patch and pushes to the project's public repository;
  • I pull the changes to my local master branch from origin/master;
  • I confirm that my patch was applied by the maintainer.

Problem:

  • git branch -d won't now delete my bug fix branch, giving error: The branch ... is not fully merged.

However, the patch that the maintainer applied contained all the changes that I had made in my bug fix branch, so intuitively it seems wrong that Git should claim that the branch "is not fully merged."

The Git book seems to agree with me. It mentions only one reason why git branch -d would fail - because the branch "contains work that isn’t merged in yet" - which doesn't apply in my case.

I am unsure whether I have encountered a bug in Git, or whether Git just doesn't support my use-case.

Yes, I could just go ahead and delete the branch using git branch -D, but I would rather not get into the habit of doing that except in cases where I want to delete a branch whose changes to the files in the working tree really have not been merged into any other branch.

I would much rather:

  • understand why Git is claiming that the bug fix branch "is not fully merged" even though the branch's changes have indeed been applied to master, and
  • find a more elegant response than resorting to git branch -D.

Example

$ git clone https://git.example.com/repo.git
# Output omitted for brevity. Clone proceeded fine.
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean
$ git checkout -b fix_bug_42
Switched to a new branch 'fix_bug_42'
$ vim foo.txt # Fix bug 42.
$ git add foo.txt
$ git commit -m "Fix bug 42"
[fix_bug_42 244540f] Fix bug 42
 1 file changed, 1 insertion(+)
$ git format-patch HEAD^
0001-Fix-bug-42.patch

I then email 0001-Fix-bug-42.patch to the maintainer.

The maintainer applies this using git am < 0001-Fix-bug-42.patch, maybe makes some other commits too, and pushes to origin/master.

I then do:

$ git remote update
Fetching origin
remote: Counting objects: 54, done.               # Numbers illustrative only
remote: Compressing objects: 100% (42/42), done.
remote: Total 42 (delta 29), reused 0 (delta 0)
Unpacking objects: 100% (42/42), done.
From https://git.example.com/repo
   7787ce5..1c1a981  master     -> origin/master
$ git status
On branch fix_bug_42
nothing to commit, working directory clean
$ git co master
Switched to branch 'master'
Your branch is behind 'origin/master' by 3 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)
$ git pull
Updating 7787ce5..1c1a981
Fast-forward
 bar.txt          |   3 +--
 contributors.txt |   1 +
 foo.txt          |   1 +
 3 files changed, 3 insertions(+), 2 deletions(-) # Numbers illustrative only

So far, so good! Let's see if my patch was applied in one of those three commits:

$ git log @...@^^^ # Show the last two commits
commit 1c1a981f0b9cbaa593c949cea07e3265e2f8c9fa
Author: A Maintainer <maintainer@example.com>
Date:   Thu Aug 11 20:58:32 2016 +0000

    Add sampablokuper to list of contributors

commit 44a35eae3dc69002b6d3484cd17ad653ee7de3c3
Author: Sam Pablo Kuper <sampablokuper@example.edu>
Date:   Thu Aug 11 19:00:00 2016 +0000

    Fix bug 42

commit 25c3562fecd3f42f76c7552fabec440dd4473c6e
Author: A Maintainer <maintainer@example.com>
Date:   Thu Aug 11 14:44:53 2016 +0000

    Edit bar.txt

Looks like it was applied in the penultimate commit. Let's confirm that!

$ diff -s <(git diff @^^ @^) <(git diff fix_bug_42^ fix_bug_42)
Files /dev/fd/63 and /dev/fd/62 are identical
$

All the changes I made in my fix_bug_42 branch have definitely been applied and committed by the maintainer. Yay!

That means Git should know it is now safe for me to delete my bug fix branch, right? Wrong!

$ git branch -d fix_bug_42
error: The branch 'fix_bug_42' is not fully merged.
If you are sure you want to delete it, run 'git branch -D fix_bug_42'.

Argh!

Hypotheses

Now, I think I know why this is happening. I think it is because the commit in master in which my patch was applied (44a35eae3dc69002b6d3484cd17ad653ee7de3c3) has a different hash to the commit in fix_bug_42 from which that patch was made (244540f...). I.e. Git thinks that because commit 244540f doesn't exist in master, that means fix_bug_42 "is not fully merged".

Questions

  • Is my hypothesis correct?

  • Regardless, what can I do about this problem, short of using git branch -D? (E.g. is there a better workflow I could have used, that would have avoided this problem?)

  • Does this unexpected (to me, at least) behaviour from Git represent either a bug in Git, or at least a legitimate feature request for improved handling of this use case?

1 Answers1

1

Your hypothesis is correct, a better workflow would have been using something like git request-pull:

https://git-scm.com/docs/git-request-pull

There's no way for git to know that your commit is the same as the commit that was merged without doing a lot of detailed checking, so it's not exactly a bug.

Plasma
  • 2,369
  • 1
  • 29
  • 35
  • *"a better workflow would have been using something like `git request-pull`"*. Thanks :) Please could you expand on that to explain how this would solve the problem? Wouldn't the maintainer still be the committer of the patch when merging it into their repo, and if so, then given that [the commit hash is based partly on who the committer is](https://gist.github.com/masak/2415865), wouldn't the merge commit's hash still differ from the one in my local branch (as in my example), even though the diffs would (as also in my example) be identical? –  Aug 13 '16 at 02:12
  • If I'm not mistaken, then they will commit a merging of your commit into their repo - so your commit hash will be in the repo's history. It would work similar to a github PR. – Plasma Aug 13 '16 at 02:27
  • Thanks. I'm still puzzled by this, though. It seems `git format-patch` should include [equivalent metadata to that transferred in a `git pull`](http://stackoverflow.com/q/4624127/82216). Plus, the Linux kernel & Git teams have many developers [using `git format-patch`](https://github.com/git/git/blob/master/Documentation/SubmittingPatches), & surely some of them would have run into this problem by now! So I feel like `git request-pull` can't be the only answer. What are the Linux devs doing that enables them to use `git format-patch` over email, without running into the same problem I did? –  Aug 13 '16 at 04:11
  • Maybe the maintainer didn't apply the patch correctly? I'm not really sure what to tell you since there is limited information. – Plasma Aug 13 '16 at 21:08