Jose Juan MontielJose Juan Montiel

Git & Merge & Rebase & Flow

On this occasion we will perform a series of operations on a git repository. We will try to simulate typical situations of group work, with different Ways to focus the union of the different jobs.

Work with two branches without common ancestors.

First, initialize a local repo, with the option of being shared by Multiple users.

cd ~/sandbox-git
git init --bare --shared ~/sandbox-git/test-repo.git
git clone test-repo.git repo-clone1
git clone test-repo.git repo-clone2

Then we go to the directory 1, and we add a first file, in the commit We indicate a username: User1.

cd repo-clone1
touch README
git add .
git commit --author="User1 <user1@git.com>" -m "Initial commit"
git push -u origin master

To view from console we can add the following to ~ / .gitconfig under [alias]

tree = log --graph --full-history --all --color --pretty=tformat:%x1b[31m%h%x09%x1b[32m%d%x1b[0m%x20%s%x20%x1b[33m(%an)%x1b[0m

The repo is thus.

git 01

Then we go to the second folder and we only do "fetch", and we create the same folder File that the other user created. We did it, then we tried to push.

cd ../repo-clone2
git fetch
vi README
git add README
git commit --author="User2 <user2@git.com>" -m "Other user change"
git push

But we get the following error.

	 ! [rejected]        master -> master (non-fast-forward)
	error: failed to push some refs to '/home/jose/sandbox-git/test-repo.git'
	hint: Updates were rejected because the tip of your current branch is behind
	hint: its remote counterpart. Integrate the remote changes (e.g.
	hint: 'git pull ...') before pushing again.
	hint: See the 'Note about fast-forwards' in 'git push --help' for details.
You can not push but you pull …​ at this time the tree is like this …​
git 02
git pull
The default behavior has changed since git 2.9

"git merge" used to allow merging two branches that have no common base by default, which led to a brand new history of an existing project created and then get pulled by an unsuspecting maintainer, which allowed an unnecessary parallel history merged into the existing project. The command has been taught not to allow this by default, with an escape hatch --allow-unrelated-histories option to be used in a rare event that merges histories of two projects that started their lives independently.

In this case, we have started from 2 independent branches, and the pull of a branch (Where we already have a commit) causes a kind of "merge". In this case (for Not having common ancestors) we must do it with:

git pull origin master --allow-unrelated-histories

And this is left

git 03

Now we can do:

git push

And so is the repo:

git 04

Work in a branch, without having updated it

Let’s now test in directory 1, create a git-flow feature, create a New file and then …​ create another feature in directory 2, creating another file.

Then in directory 1, we close the feature, then in directory 2.

We merge develop into our branch, and then merge our branch into develop (closing the feature).
cd ../repo-clone1
git flow init
git flow feature start user1

vi feature-user1.txt
git add feature-user1.txt
git commit --author="User1 <user1@git.com>" -m "Commit in feature-user1"
git 05
But we "forgot" to do fetch / pull before we start …​ and here begins the lesson …​
git fetch
git pull origin master

Now (about to finish the story) we have the tree as well.

git 06

We close feature

git flow feature finish

Summary of actions:

  • The feature branch 'feature/user1' was merged into 'develop'

  • Feature branch 'feature/user1' has been locally deleted

  • You are now on branch 'develop'

Let’s upload develop (by default git-flow does not push), which we have not done yet.
git push origin develop

Leaving the tree, like this:

git 07

Keep branch updated with changes from develop

Now we go to the directory 2, we update, create a feature, We do something in the file README comiteamos, but before closing the feature, Change folder 1 and modify the README, we return to the original directory and Before closing feature, mergeamos develop in our branch.

cd ../repo-clone2
git fetch
git checkout develop
git pull

git flow init

git flow feature start user2

vi feature-user2.txt
git add  feature-user2.txt
git commit --author="User2 <user2@git.com>" -m "Commit in feature-user2"

cd ../repo-clone1
vi feature-user1.txt
git add  feature-user1.txt
git commit --author="User1 <user1@git.com>" -m "Update develop file feature1"
git push origin develop

Here (folder 1) we still do not know anything about the feature2, leaving the tree as well

git 08

The "problem" with overshoot is that it alters the story lines, for better or for worse.

If we follow the classic approach, to keep our feature2 current, we must Often merge with develop, or at least, before closing the feature.

cd ../repo-clone2
git fetch
git pull origin develop

This, as written, performs a direct merge, since we are pulling another Branch (develop) in the of the feature.

A pull of a branch in another that is not yours, is equivalent to a merge.
git 09

Merge, after a pull (update), from one branch to another

A way, from my point of view, more controlled to focus the merge (or Either by having a branch updated, or by mixing it later in another), Could have been: a fetch to update, changing to develop, Do pull and then return to the feature and doing merge.

Keep branch updated with an override.

But, try again (we go to folder 1, we make change in develop and we raise it), But this time we make a rebase (using git flow)

cd ../repo-clone1
vi feature-user1.txt
git add  feature-user1.txt
git commit --author="User1 <user1@git.com>" -m "Update develop file feature1"
git push origin develop
cd ../repo-clone2
git fetch
git flow feature rebase user2

Will try to rebase 'user2' which is based on 'develop'…​ First, rewinding head to replay your work on top of it…​ Applying: Commit in feature-user2 Applying: Update develop file feature1

git 10
git flow feature finish

Branches 'develop' and 'origin/develop' have diverged. Fatal: And branch 'develop' may be fast-forwarded.

This only means that the branch where we are working is not present (similar to the first example).

git checkout develop

Switched to branch 'develop'
Your branch is behind 'origin/develop' by 2 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)

git pull
Updating 408fcbf..f76cade
Fast-forward
 feature-user1.txt | 3 +++
 1 file changed, 3 insertions(+)

git checkout feature/user2
Switched to branch 'feature/user2'

git flow feature finish
Switched to branch 'develop'
Your branch is up-to-date with 'origin/develop'.
Auto-merging feature-user1.txt
CONFLICT (content): Merge conflict in feature-user1.txt
Automatic merge failed; fix conflicts and then commit the result.

There were merge conflicts. To resolve the merge conflict manually, use:
    git mergetool
    git commit

You can then complete the finish by running it again:
    git flow feature finish user2

The rebase has generated conflicts, which at the time of making the merge, gives us problems.

git mergetool
git 11

It alters the timeline of the branch where it does it. We resolve the conflict and make changes.

git add feature-user1.txt
git commit --author="User2 <user2@git.com>" -m "Resolve rebase y merge"
git push origin develop

We have this tree

git 12

Git tree police.

If we consider that a branch is created for a functionality, and the whole set Of commits that are related to it, including merge from develop, should not Be reflected in the final tree after the merge, --no-ff is our friend.

We create a feature3, make some changes, make some changes in develop, Let’s take it to the branch of feature3, and when it’s done, merge with no-ff.

cd ~/sandbox-git/repo-clone1
git fetch
git pull origin develop

cd ~/sandbox-git/repo-clone2
git fetch
git pull origin develop

git flow feature start feature3
vi feature3-user2.txt
git add feature3-user2.txt
git commit --author="User2 <user2@git.com>" -m "File from feature 3"

cd ~/sandbox-git/repo-clone1
vi change-in-develop.txt
git add change-in-develop.txt
git commit --author="User1 <user1@git.com>" -m "change-in-develop"
git push origin develop

cd ~/sandbox-git/repo-clone2
git checkout develop
git fetch
git pull
git checkout feature/feature3
git merge develop

The tree we have so …​ now after the merge, make another change and reintegrate In develop as a single commit.

git 13
vi feature3-another-user2-file.txt
git add feature3-another-user2-file.txt
git commit --author="User2 <user2@git.com>" -m "File from feature 3"
git checkout develop
git merge feature/feature3 --no-ff
git push origin --delete feature/feature3
git branch -d feature/feature3
git 14

Let’s observe how clean the branch develop

git merge no ff

References: