wiki:GitCreateBundle

Version 16 (modified by Piero Campalani, 13 years ago) ( diff )

wrong hash tags

Back to [ProvideFix 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 [StandardsAndGuideLines 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 "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

Note: See TracWiki for help on using the wiki.