Introduction to Git –- Fall 2023
Learn More →
Learn More →
Learn More →
Learn More →
Not starting with "R"
checkout
- go/move HEAD to branch or specific commit (hash)
clean
- clear unstaged files
--dry-run
to check what will happendiff
- compare
--staged
- commit vs stagedHEAD
- commit vs working treelog
- history of commit tree
--graph
- graphically see the branchesshow
- shows info for commitstash
- temporarily stores the staged changes to the working treeswitch
- go to branch only (more clear for this use)tag
- tag (like version number), naming commits (not branches)Starting with "R"
rebase -i HEAD~3
- interactively rebase last 3 commits
squash
- summarize last 3 commitsreflog
- log of commits that changes the headreset
- as checkout
but takes options to also do updates
<filename>
- unstages the filerestore
- restore file in work dir
--staged
- unstagesrevert
- makes inverse of the previous commit. The commit tree is not modified, rather two cancelling commits.rev-parse --short
- find short hash for references, like HEAD~~
rm
- remove from repoThere 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. (Time goes rightwards)
Usually, a branch is created to work on a new feature. Once the feature is completed, it is merged back with the master branch.
(Time goes right)
Creating a new branch does not change the repository, it just points out 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?
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>
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
Here we create a merge conflict:
$ mkdir merge-test
$ cd merge-test/
~/merge-test$ git init
Initialized empty Git repository in /home/bbrydsoe/merge-test/.git/
~/merge-test$ echo "Creating a file with some text to play with." >> myfile.txt
~/merge-test$ git add myfile.txt
~/merge-test$ git commit -m "First commit"
[master (root-commit) 9badcc6] First commit
1 file changed, 1 insertion(+)
create mode 100644 myfile.txt
~/merge-test$ git checkout -b mergebranch
Switched to a new branch 'mergebranch'
~/merge-test$ echo "Adding text to the file in order to merge." > myfile.txt
~/merge-test$ git add myfile.txt
~/merge-test$ git commit -m "Changed the content of myfile.txt"
[mergebranch 41b0e36] Changed the content of myfile.txt
1 file changed, 1 insertion(+), 1 deletion(-)
~/merge-test$ git checkout master
Switched to branch 'master'
~/merge-test$ echo "Put more text to the file" >> myfile.txt
~/merge-test$ git add myfile.txt
bbrydsoe@enterprise-a:~/merge-test$ git commit -m "Added more text"
[master c17e479] Added more text
1 file changed, 1 insertion(+)
~/merge-test$ git merge mergebranch
Auto-merging myfile.txt
CONFLICT (content): Merge conflict in myfile.txt
Automatic merge failed; fix conflicts and then commit the result.
So Git complains
We can get some more information with the git status
command:
~/merge-test$ 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")
Looking inside the file myfile.txt:
~/merge-test$ cat myfile.txt
<<<<<<< HEAD
Creating a file with some text to play with.
Put more text to the file
=======
Adding text to the file in order to merge.
>>>>>>> mergebranch
Some "conflict dividers" have been added.
$ git merge --continue <branch-to-merge>
Commands to help:
git status
git log --merge
git diff
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 <other-branch>
Success!
$ git merge <other-branch>`
$ git merge --continue <other-branch>`
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]
}
(Time goes leftwards)
Rebasing 'bugfix' to 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]
}
(Time goes leftwards)
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
Not the same! A rebase moves a branch from one base to another. A fast-forward merge moves a branch head from the current commit to a commit for a descendant.
Example:
Start
digraph {
rankdir=LR
"commit2" -> "commit1"
"commitX" -> "commit2"
"commit3" -> "commit2"
"commit4" -> "commit3"
"commit5" -> "commit4"
"commit6" -> "commit5"
"commitY" -> "commitX"
"commitZ" -> "commitY"
"A" -> "commit4" [style=dashed]
"B" -> "commitZ" [style=dashed]
"C"-> "commit6" [style=dashed]
A [shape=plaintext]
B [shape=plaintext]
C [shape=plaintext]
}
(Time goes leftwards)
Rebase B onto C
digraph {
rankdir=LR
"commit2" -> "commit1"
"commit3" -> "commit2"
"commit4" -> "commit3"
"commit5" -> "commit4"
"commit6" -> "commit5"
"commitX'" -> "commit6"
"commitY'" -> "commitX'"
"commitZ'" -> "commitY'"
"A" -> "commit4" [style=dashed]
"B" -> "commitZ'" [style=dashed]
"C"-> "commit6" [style=dashed]
A [shape=plaintext]
B [shape=plaintext]
C [shape=plaintext]
}
FF merge C into A:
digraph {
rankdir=LR
"commit2" -> "commit1"
"commitX" -> "commit2"
"commit3" -> "commit2"
"commit4" -> "commit3"
"commit5" -> "commit4"
"commit6" -> "commit5"
"commitY" -> "commitX"
"commitZ" -> "commitY"
"A" -> "commit6" [style=dashed]
"B" -> "commitZ" [style=dashed]
"C"-> "commit6" [style=dashed]
A [shape=plaintext]
B [shape=plaintext]
C [shape=plaintext]
}
(Time goes leftwards)
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>
Apply the commit Y to the master branch (called Y´)
digraph {
rankdir=LR
"commit2" -> "commit1"
"commitX" -> "commit2"
"commit3" -> "commit2"
"commitY'" -> "commit3"
"commitY'" -> "commitY"
"commitY" -> "commitX"
"commitZ" -> "commitY"
"master" -> "commitY'" [style=dashed]
"Feature" -> "commitZ" [style=dashed]
master [shape=plaintext]
Feature [shape=plaintext]
}
(Time goes leftwards)
git branch
git checkout
or git switch
git merge
rebase
- like merge but end result is just one branchConflict?
stash
or discard (clean
) the changes before switching branch or do a checkout --merge
.Workflow merge
$ git merge <other-branch>
Conflict?
$ git merge --continue <other-branch>
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"