wiki:GitCreateBundle

Back to How To Provide A Fix.

Why git bundles

MERGE BRANCHES VIA PATCH MANAGER.

Currently, the repo is folded into different public branches, letting a contributor choose on which of these branch his new patch is meant to be applied (see here for details).

This wiki page is meant for developers who need to reflect a merge operations to the public repo: indeed a solution which provides both un-linearized history and submission to the patch manager is to submit a bundle.

This allows no behind-the-scenes merge operations, and at the same time reflects the actual merge in the history of the repo, which otherwise won't be visible with patches:

  • REPO STATUS BEFORE MERGE :
      o-------A------B   master
               \           
                C-------D   feature_X
    
  • REPO STATUS AFTER MERGING BUNDLE :
      o-------A------B------M   master
               \           /
                C---------D   feature_X
    
  • REPO STATUS AFTER MERGING PATCH :
      o-------A------B------P   master
               \          
                C---------D   feature_X
    

Although git bundles are pretty powerful, that is they can add commits, merge branches, etc. It is important here to follow some rules when submitting a bundle, which mainly are:

  • 1 HEAD:
    Each patch must be associated to a ticket (see guidelines), hence a git bundle should only perform the merge of two branches: no new "patches" should be stacked in, in none of the two branches (number of refs in the bundle will be checked anyway by the patch manager);
  • NAMING CONVENTIONS
    • the name of the merging commit must follow the naming convention "ticket:NNN - Merge branch 'feature_X' into master", so actually leave the default title that git sets;
    • the name of the bundle file must follow the pattern "ticket:NNN_descriptionOfMerge.bundle", referring to the ticket which justifies the merge.

Creating and submitting a bundle

When it's time to merge a branch which is publicly available, the situation can be very painful, but this is another story.

First of all, the status of the two branches to be merged must be the _exactly_ the same before starting to merge. In this case, we can imagine we want to merge feature_X into master:

  o-------A-------B  master
           \
            D-------E  feature_X

Going to the terminal, this means:

$ git fetch origin
# already up-to-date? otherwise pull new changes on both branches.

$ git branch
  feature_X
* master
  release_8.4
  release_8.5
$ git merge feature_X --no-ff
# ..and we assume there is no conflict, see 

# Check:
$ git log --oneline -1 master
3f36d7f Merge branch 'feature_X' into master

After the merge, the situation (locally) is then:

  o-------A--------------M  master
           \            /
            C----------D  feature_X

To publish this on the public repo you can create the bundle, but first (again) remember that:

  • local snapshot of two branches must exactly be synchronized with that of the public repo;
  • consequently, the names of the merging branches must be the same as well (developers tend to open new branches for local development...).

If all is fine, create the bundle:

  • Synopsis:
    $ git bundle create <bundle_name> <branch_name> <refs>
    
  • Example:
    # A graph view first:
    $ git log master --graph -4
    *   commit 3f36d7f1a19c3e4cfeab66807dd39f6d7fda771b
    |\  Merge: 5ee465b f7ed86b
    | | Author: Willie The Merger <w.merger@jacobs-university.de>
    | | Date:   Tue Oct 15 12:37:53 2013 +0200
    | | 
    | |     Merge branch 'feature_X' into master
    | |   
    | * commit f7ed86b6eba15e141202b44279d7f7afa4484944
    | | Author: Jack Shortie <j.shortie@jacobs-university.de>
    | | Date:   Tue Oct 8 12:34:54 2013 +0200
    | | 
    | |     D
    | |   
    | * commit 79e8805fe4b4ba2502be9c0e4b312b8b97e739b9
    |/  Author: Johnnie Cannuccia <j.cnnuccia@acme.org>
    |   Date:   Tue Oct 8 12:21:58 2013 +0200
    |   
    |       C
    |  
    * commit 5ee465b1ca5306590dce61e97acb97f1e88aa3c1
    | Author: Piero Campalani <p.campalani@jacobs-university.de>
    | Date:   Thu Aug 8 14:24:32 2013 +0200
    | 
    |     A
    
    
    # Select the commits to be bundled 
    # (e.g. use '^<first_common_ancestor>', which in our case is A (changeset:5ee465))
    $ git log master ^5ee465 --oneline
    3f36d7f Merge branch 'ProvaMerge' into ProvaMergeMaster
    f7ed86b D
    79e8805 C
    
    # Create the bundle
    $ git bundle create ticket:XYZ_MergingFeature_xOnMaster.bundle master ^5ee465
    Counting objects: 14, done.
    Delta compression using up to 2 threads.
    Compressing objects: 100% (9/9), done.
    Writing objects: 100% (9/9), 838 bytes, done.
    Total 9 (delta 8), reused 0 (delta 0)
    
    # See inside it:
    $ git list-heads ticket:XYZ_MergingFeature_xOnMaster.bundle
    3f36d7f1a19c3e4cfeab66807dd39f6d7fda771b refs/heads/master
    

Verifying the bundle

In case you need to locally verify that you produced a good bundle, you can create a new local repo and test it:

# Create/init new empty repo
$ echo `pwd`
/home/rasdaman/rasdaman
$ mkdir ~/test_repo
$ cd ~/test_repo
$ git init

# Reset the history to where it was /before/ merging (A commit)(simulating public repo status)
$ cd /home/rasdaman/rasdaman
$ git remote add test_remote ~/test_repo
$ git checkout master
$ git reset --hard 5ee465

# Push two branches to the test repo
$ git push test_remote master
$ git push test_remote feature_X

# Now move to the test repo and checkout on a branch you're not merging into:
$ cd ~/test_repo
$ git branch
  feature_X
* master
$ git checkout feature_x
$ git branch
* feature_X
  master

# Apply the bundle: [verify &] apply
$ git bundle verify /path/to/ticket:XYZ_MergingFeature_xOnMaster.bundle
$ git fetch /path/to/ticket:XYZ_MergingFeature_xOnMaster.bundle master:master

Fixing conflicts

During a merge, you can often stumble upon one, two or several thousands of conflicts:

$ git merge feature_X --no-ff
...
Automatic merge failed; fix conflicts and then commit the result.

In this case I would first suggest to configure git to present the conflicts with the so-called diff3 style (thanks CoolAJ86), which is:

<<<<<<<
Changes made on the branch that is being merged into. In most cases,
this is the branch that I have currently checked out (i.e. HEAD).
|||||||
The common ancestor version.
=======
Changes made on the branch that is being merged in. This is often a 
feature/topic branch.
>>>>>>>

It really helps you better understand the conflicts. You can configure it with:

$ git merge --abort
$ git config merge.conflictstyle diff3

Then, as usual, fix conflicts, and commit:

$ git merge feature_X --no-ff
...
Auto-merging Makefile.am
CONFLICT (content): Merge conflict in Makefile.am
Automatic merge failed; fix conflicts and then commit the result.
$ vim Makefile.am
...fix...
$ git add Makefile.am
$ git commit

...and now you can proceed with the bundle.

I don't see any merge commit

..you probably either forgot to add the --no-ff option when calling the merge (by default, but only if the merge is fast-forward, a normal single-parent commit is created), or somehow your .git/MERGE_HEAD passed away.

This means you have your merge commit with lots of conflicts fixed that you want to re-apply. One idea is to:

  • save your merge to a patch, say MERGE.patch
  • reset to a pre-merge state and re-merge
  • if you have conflicts, just git-add everything to make git think you solved them, then commit
  • revert the (fake) commit and format a patch out of it, say REVERT.patch
  • reset again to a pre-merge state and again re-merge
  • re-git-add everything to mark conflicts solved (even though they aren't), but don't commit this time
  • apply REVERT.patch then MERGE.patch in the index
  • the two patches are applied, now you can git-commit.

Let's say you applied a merge M of feature_X into master, but you see no merge commit in your history:

  o-------A--------------M  master
           \            
            C----------D  feature_X
# Patch your merge commit:
$ git branch
* master
  feature_X
$ git format-patch HEAD~
MERGE.patch
# Save the log for final checks
$ git log -1 master > MERGE_FF.log

# Re-merge
$ git reset --hard A
$ git merge --no-ff feature_X
...
Automatic merge failed; fix conflicts and then commit the result.

# Mark conflicts as solved, without applying changes
$ git add folder1/ folder2/ ...
$ git commit 
# let's call it M'

# Revert your fake merge
$ git revert -m 1 M'
# let's call this commit rM'

# Patch the reversion
$ git format-patch HEAD~
REVERT.patch

Graphically, this is the current situation:

  o-------A--------------M'----------rM'  master
           \            /
            C----------D  feature_X

Now we roll-back to a pre-merge state again, then we merge, revert, and apply our initial patch: (M'+rM'+M) = (M'-M'+M) = M

# Re-merge
$ git reset --hard A
$ git merge --no-ff feature_X
...
Automatic merge failed; fix conflicts and then commit the result.

# Mark conflicts as solved, without applying changes, but don't commit now
$ git add folder1/ folder2/ ...
# ... now we are @ M', without explicit commit

# Apply rM' and M (without commiting)
$ git apply --index REVERT.patch
$ git apply --index MERGE.patch

# Commit
$ git commit
[now you can customize the commit title]

Finally, we have our original merge, with true conflicts solution, but as a merge commit:

  o-------A--------------M  master
           \            /
            C----------D  feature_X

Verify the operation:

$ git show M --stat > MERGE.log
$ diff MERGE_FF.log MERGE.log
1,2c1
< commit <SHA-1 of M no-fast-forward>
< Merge: A D
---
> commit <SHA-1 of M fast-forward>
4c3
< Date:   Wed Oct 23 17:24:04 2013 +0200
---
> Date:   Wed Oct 23 15:55:02 2013 +0200

If only the hash tags of the commits and the dates differ, then you applied exactly the same changes you made in your first merge.

Further reading

Last modified 4 years ago Last modified on Oct 25, 2013 11:53:35 AM