Introduction to Git –- Fall 2024
Learn More →
Learn More →
Learn More →
Learn More →
There are many uses for branches:
Until now, we have worked with a repository that only have one branch, with the commits done one at a time:
In the above picture, the master branch points to a commit. The current position is HEAD. (Time goes rightwards)
Now we want to look at repositories with several branches:
Branches are used to create another line of development. They are "individual projects" within a git repository.
Usually, a branch is created to work on a new feature. Once the feature is completed, it is merged back with the master branch.
Creating a new branch does not change the repository, it just adds a new reference to the commit.
Note that the branch is created from the current HEAD.
To create a new branch (called cool-feature in the following):
$ git branch cool-feature
To move to another branch (switch):
$ git checkout cool-feature
or…
$ git switch cool-feature
If you wish to switch to a new branch that is not yet created, you can do so by adding the flag -b
to git checkout
.
To see which branch you are on:
$ git branch
$ git checkout master
$ git merge cool-feature
$ git branch -d cool-feature
$ mkdir my-project; cd my-project/
$ git init
Initialized empty Git repository in /home/bbrydsoe/my-project/.git/
$ touch file.txt
$ git add file.txt
$ git commit -m "Committing the first file"
[master (root-commit) 1006b51] Committing the first file
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file.txt
$ git branch cool-feature
$ git checkout cool-feature
Switched to branch 'cool-feature'
$ echo "This is a text" > file.txt
$ git add file.txt
$ git commit -m "Added text to the first file"
[cool-feature 5bad966] Added text to the first file
1 file changed, 1 insertion(+)
$ git checkout master
Switched to branch 'master'
$ echo "Text to the second file" > second-file.txt
$ git add second-file.txt
$ git commit -m "Added a second file"
[master bdec2cf] Added a second file
1 file changed, 1 insertion(+)
create mode 100644 second-file.txt
$ git log --graph --oneline --decorate --all
$ git graph
$ git config --global alias.graph "log --all --graph --decorate --oneline"
This is on the master branch
$ git graph
* bdec2cf (HEAD -> master) Added a second file
| * 5bad966 (cool-feature) Added text to the first file
|/
* 1006b51 Committing the first file
We now merge the branches and check again
$ git merge cool-feature
Merge made by the 'recursive' strategy.
file.txt | 1 +
1 file changed, 1 insertion(+)
$ git graph
* cf3e6b7 (HEAD -> master) Merge branch 'cool-feature'
|\
| * 5bad966 (cool-feature) Added text to the first file
* | bdec2cf Added a second file
|/
* 1006b51 Committing the first file
Now we can delete the new branch we had created, since all the content is now in the master branch.
$ git branch -d cool-feature
Deleted branch cool-feature (was 5bad966).
Comment: It is good practice to keep old branches for understanding of the development. Deletion could however be done for very evident mistakes or insignificant issues.
In a somewhat nicer format, it looks like this:
We commit stuff to both branches
digraph {
rankdir=LR
"commitX" -> "commit1"
"commit2" -> "commit1"
"commitY" -> "commitX"
"commit3" -> "commit2"
"master" -> "commit3" [style=dashed]
"cool-feature" -> "commitY"[style=dashed]
master [shape=plaintext]
"cool-feature" [shape=plaintext]
}
(Time goes leftwards)
Merge 'cool-feature' to 'master'
digraph {
rankdir=LR
"commit2" -> "commit1"
"commitX" -> "commit1"
"commit3" -> "commit2"
"commitY" -> "commitX"
"master" -> "commit4" [style=dashed]
"cool-feature" -> "commitY" [style=dashed]
"commit4" -> "commit3"
"commit4" -> "commitY"
master [shape=plaintext]
"cool-feature" [shape=plaintext]
}
Delete 'cool-feature'
digraph {
rankdir=LR
"commit2" -> "commit1"
"commitX" -> "commit1"
"commit3" -> "commit2"
"commitY" -> "commitX"
"master" -> "commit4" [style=dashed]
"commit4" -> "commit3"
"commit4" -> "commitY"
master [shape=plaintext]
}
(Time goes leftwards)
As mentioned above, you switch between branches with:
$ git checkout <branch>
What happens if you have uncommitted changes (and/or new files added) when you try to switch?
The uncommitted changes will be carried to the new branch that you switch to, if possible.
Changes that you commit will be committed to the newly switched branch.
What if there is a conflict?
We continue in the same repository!
Here we create a new branch, switch to it, then add a new file. Then we switch back to the master branch without committing the changes:
$ git checkout -b cool-feature
Switched to a new branch 'cool-feature'
$ touch newfile.txt
$ git add newfile.txt
$ git checkout master
A newfile.txt
Switched to branch 'master'
Git warns that there is a file added (A
) in one branch but not the other, but the switch is allowed.
We continue in the same repository!
First commit the newfile.txt
in the cool-feature branch to clean the environment.
If we make changes to the file in one of the branches (go back to cool-feature
) but not on the other and do not commit it, then git will again warn:
$ git switch cool-feature
$ git commit -m "newfile.txt"
$ echo "Adding some text" >> second-file.txt
$ git add second-file.txt
$ git checkout master
M second-file.txt
Switched to branch 'master'
Git warns that there is a file that is modified (M
) in one branch but not the other, but the switch is allowed.
We continue in the same repository!
Assume two branches, "cool-feature" and "morefeatures"
Create the branch "morefeatures" without switching to it
Switch to branch "cool-feature", add some text to a file, stage the file and commit it:
$ git branch morefeatures
$ git checkout cool-feature
Switched to branch 'cool-feature'
$ git commit -m "second-file.txt"
$ echo "add text" >> morefiles.txt
$ git add morefiles.txt
$ git commit -m "Some text"
[cool-feature 469542b] Some text
1 file changed, 1 insertion(+)
create mode 100644 morefiles.txt
Switch to branch "morefeatures". Modify the same file, stage the file and commit it. Then try and switch back to the "cool-features" branch:
$ git checkout morefeatures
Switched to branch 'morefeatures'
$ echo "Adding yet some more text" >> morefiles.txt
$ git add morefiles.txt
$ git checkout cool-feature
error: Your local changes to the following files would be overwritten by checkout:
morefiles.txt
Please commit your changes or stash them before you switch branches.
Aborting
Now Git complains and do not allow the switch.
So, what can we do if there is a conflict?
The command "stash" can be described as a drawer where you store uncommitted changes temporarily.
After stashing your uncommitted changes you can continue working on other things in a different branch.
The uncommitted changes that are stored in the stash can be taken out and applied to any branch, including the original branch.
First do a git status
in the branch where you may have uncommitted changes:
$ git status
On branch morefeatures
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: file.txt
new file: morefiles.txt
You can see the dirty status.
To fix it, let us use git stash
:
$ git stash
Saved working directory and index state WIP on morefeatures: 4922606 Some text
Checking again with git status
:
$ git status
On branch morefeatures
nothing to commit, working tree clean
You can now switch branches and work on something else.
You can have several stashes stored. To see them, use
$ git stash list
Example:
$ git stash list
stash@{0}: WIP on morefeatures: 4922606 Some text
stash@{1}: WIP on morefeatures: 4922606 Some text
stash@{2}: WIP on morefeatures: 4922606 Some text
When you have done what you needed before committing the stashed changes you can reapply a stash (select branch first), using
$ git stash apply
which will apply the most recent stash. If you want to apply a different stash, you can name it.
Example:
$ git stash apply stash@{0}
If you do not want to stash your changes, but just get rid of them, you can use git clean
.
WARNING: This command will remove all non-tracked files in your current directory!
You can safely test which files will be removed by running:
$ git clean --dry-run
--merge
(or -m
):$ git checkout --merge <branch>
This will perform a three-way merge between your working tree and the new branch, with the current branch as the base.
After the merge, you will be on the new branch and the merged result will be in your working tree.
NOTE: As with any merge, conflicts may result and you will then have to resolve those.
error: Entry '<fileName>' would be overwritten by merge.
Cannot merge. (Changes in staging area)
git branch
.Git can automatically try to merge when you give the command:
$ git merge <branch-to-merge-into-present-branch>
while standing on the branch you want to merge to.
The most commonly used
Let's create a merge conflict:
$ mkdir merge-test
$ cd merge-test/
$ git init
$ echo "Initial content" > myfile.txt
$ git add myfile.txt
$ git commit -m "first commit"
$ git checkout -b feature_1
$ echo "Feature 1 is a good implementation" >> myfile.txt
$ git commit -a -m "start work on feature 1"
$ git checkout master
$ echo "Working on a really cool feature" >> myfile.txt
$ git commit -a -m "start work on a cool feature"
We now have two branches, master and feature_1:
$ git log --all --graph --decorate --oneline
* d8e6809 (HEAD -> master) start work on a cool feature
| * 87934eb (feature_1) start work on feature 1
|/
* ce7e46c first commit
What are the contents of myfile.txt in the two branches?
$ git diff master feature_1 -- myfile.txt
diff --git a/myfile.txt b/myfile.txt
index b14ae98..5390ea7 100644
--- a/myfile.txt
+++ b/myfile.txt
@@ -1,2 +1,2 @@
Initial content
-Working on a really cool feature
+Feature 1 is a good implementation
Or use the git show <ref>:
$ git show master:myfile.txt
Initial content
Working on a really cool feature
$ git show feature_1:myfile.txt
Initial content
Feature 1 is a good implementation
Let's try to merge the feature_1 branch on to the master branch:
$ $ git merge feature_1
Auto-merging myfile.txt
CONFLICT (content): Merge conflict in myfile.txt
Automatic merge failed; fix conflicts and then commit the result.
The merge failed due to a conflict. In this case, the conflict arises because there are changes in the same line on both branches.
We can get some more information with the git status command:
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: myfile.txt
no changes added to commit (use "git add" and/or "git commit -a")
Let's check the file that lead to the conflict, note the "conflict dividers":
$ cat myfile.txt
Initial content
<<<<<<< HEAD
Working on a really cool feature
=======
Feature 1 is a good implementation
>>>>>>> feature_1
One can abort the merge with git merge –abort.
Or one may try to solve the conflict..
$ git merge --continue
Helpful commands:
identify conflicting files: git status
list the conflicting commits among the branches: git log --merge
find differences between the commits involved in the merge: git diff
reset conflicted files to a known good state: git reset
If you made a mistake when you resolved a conflict and have completed the merge before realizing, you can roll back to the commit before the merge was done with the command git reset --hard
.
$ git merge <branch>
Success!
$ git merge <branch>
$ git merge --continue
Success!
Branch 'bugfix' was branched from 'master'
digraph {
rankdir=LR
"commit2" -> "commit1"
"commit3" -> "commit2"
"commit4" -> "commit3"
"commitX" -> "commit2"
"master" -> "commit4" [style=dashed]
"bugfix" -> "commitY" [style=dashed]
"commitY" -> "commitX"
master [shape=plaintext]
"bugfix" [shape=plaintext]
"HEAD" -> "bugfix" [style=dashed]
HEAD [shape=plaintext]
}
Rebasing 'bugfix' onto the 'master' branch
digraph {
rankdir=LR
splines="line"
"commit2" -> "commit1"
"commit3" -> "commit2"
"master" -> "commitY'" [style=dashed]
"commit4" -> "commit3"
"commitX'" -> "commit4"
"commitY'" -> "commitX'"
master [shape=plaintext]
"HEAD" -> "master" [style=dashed]
HEAD [shape=plaintext]
}
Assume a master branch and the branch "cool-features" and that you want to rebase the branch "cool-features" onto the master branch:
$ git checkout cool-features
$ git rebase master
This works by
Basically, cherry-picking in Git means that you choose a commit from one branch that you apply to another.
Find the hash for the commit you want to apply, using git log
.
Then make sure you are on the right branch that you want to apply the commit to:
$ git checkout <branch>
Now you execute the cherry-picking:
$ git cherry-pick <hash>
Let's assume we have:
digraph {
rankdir=LR
"commitY" -> "commitX"
"commitZ" -> "commitY"
"master" -> "commit3" [style=dashed]
"feature" -> "commitZ" [style=dashed]
"HEAD" -> "master" [style=dashed]
"commit2" -> "commit1"
"commitX" -> "commit2"
"commit3" -> "commit2"
master [shape=plaintext]
feature [shape=plaintext]
HEAD [shape=plaintext]
}
Applying the commit Y to master with git cherry-pick <hash>
will result in a new commit Y´.
digraph {
rankdir=LR
"commitY" -> "commitX"
"commitZ" -> "commitY"
"master" -> "commitY'" [style=dashed]
"feature" -> "commitZ" [style=dashed]
"HEAD" -> "master" [style=dashed]
"commit2" -> "commit1"
"commitX" -> "commit2"
"commit3" -> "commit2"
"commitY'" -> "commit3"
master [shape=plaintext]
feature [shape=plaintext]
HEAD [shape=plaintext]
}
git branch
git checkout
or git switch
git merge
rebase
- like merge but end result is just one branchConflict?
commit
or stash
or discard (clean
) the changes before switching branch or do a checkout --merge
.Workflow merge
git merge <branch>
Conflict?
git merge --continue
Each of the exercises has a README.md file with explanations and descriptions of what to do. You can find all of them in the subdirectory 5.branches. You should do them in the below order:
Fast-forward Merge (OK): This exercise will show an example where git can do a fast-forward merge. The exercise is in the subdirectory "1.merge-ok"
Recursive/ORT Merge (OK): In this exercise you will see an example where git can automatically merge two branches. This time git will use the recursive merge. The exercise can be found in the subdirectory "2.merge-ok-recursive"
Merge (BAD): This exercise gives an example of a merge that cannot be done automatically with the merge command. The exercise can be found in the subdirectory "3.merge-bad"
Rebasing (OK): In this exercise you will try the command rebase and see that it succeeds. The exercise can be found in the subdirectory "4.rebase-ok"
Rebasing (BAD): This exercise again gives an example of rebasing two branches, but in this case the rebase fails. The exercise can be found in the subdirectory "5.rebase-bad"