Introduction to Git –- Fall 2024
Git is very useful for teamwork.
You will often have three types of branches for a project/specific release:
In these exercises we will use only a few commands. These have all been mentioned before in this course, but as a refresher I will briefly discuss a couple commands here, namely:
git fetch
: This is a primary command used to download contents from a remote repository.
git push
: This is essentially the same as running git merge main
from inside the remote repository. It is mostly used to upload local changes to a remote repository.git pull
: This will fetch the latest changes from the current branch from a remote, then apply the changes to your local copy of the branch. It is similar to doing a fetch and a merge.$ git push <remote-repo> <branch>
or in some cases just
$ git push
where the default behaviour is pushing to repository "origin" and the same branch as the local.
Before pushing:
digraph {
rankdir=LR
node [shape=circle width=0.5 fixedsize=shape]
edge [arrowhead=none][shape=none]
"a" -> "b"
a [fixedsize=true label=" "]
"b" -> "c"
b [fixedsize=true label=" "]
"c" -> "d"
c [fixedsize=true label=" "]
d [fixedsize=true label=" " style=filled fillcolor=blue]
"main" -> "d" [style=dashed arrowhead=normal]
"origin/main" -> "b" [style=dashed arrowhead=normal]
main [shape=box width=0.8 style=filled fillcolor=lightblue]
"origin/main" [shape=box width=1.2 style=filled fillcolor=lightblue]
}
After pushing:
digraph {
rankdir=LR
node [shape=circle width=0.5 fixedsize=shape]
edge [arrowhead=none][shape=none]
"a" -> "b"
a [fixedsize=true label=" "]
"b" -> "c"
b [fixedsize=true label=" "]
"c" -> "d"
c [fixedsize=true label=" "]
d [fixedsize=true label=" " style=filled fillcolor=blue]
"main" -> "d" [style=dashed arrowhead=normal]
"origin/main" -> "d" [style=dashed arrowhead=normal]
main [shape=box width=0.8 style=filled fillcolor=lightblue]
"origin/main" [shape=box width=1.2 style=filled fillcolor=lightblue]
}
Pushing a staged and committed file:
bbrydsoe@enterprise-a:~/mytestrepo$ git push origin main
Enter passphrase for key '/home/bbrydsoe/.ssh/id_rsa':
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 287 bytes | 287.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:bbrydsoe/mytestrepo.git
43110f8..5fd2960 main -> main
bbrydsoe@enterprise-a:~/mytestrepo$ git log
commit 5fd2960d91a86f8c581d7470b42b1f3814e24d73 (HEAD -> main, origin/main, origin/HEAD)
Author: Birgitte Brydsö <bbrydsoe@cs.umu.se>
Date: Sun Nov 13 15:43:05 2022 +0100
Adding a file
Fetch the given remote's copy of the current branch and merge to the local copy:
$ git pull <remote-repo>
or often just
$ git pull
If you have forgotten to pull before staging and committing new stuff, and your colleague has added something to the remote repository in between, this is a handy command:
$ git pull --rebase <remote>
It fetches the remote content but does not create a new merge commit.
Assume this situation:
digraph {
rankdir=LR
node [shape=circle width=0.5 fixedsize=shape]
edge [arrowhead=none][shape=none]
"d" -> "e"
"e" -> "a"
"e" -> "f"
a [fixedsize=true label="a"]
"a" -> "b"
"b" -> "c"
b [fixedsize=true label="b"]
c [fixedsize=true label="c"]
d [fixedsize=true label="d"]
"f" -> "g"
"main" -> "c" [style=dashed arrowhead=normal]
"origin/main" -> "e" [style=dashed arrowhead=normal]
main [shape=box width=0.8 style=filled fillcolor=lightblue]
"origin/main" [shape=box width=1.2 style=filled fillcolor=lightblue]
}
Now we do a git pull
:
digraph {
rankdir=LR
node [shape=circle width=0.5 fixedsize=shape]
edge [arrowhead=none][shape=none]
"d" -> "e"
"e" -> "a"
"e" -> "f"
a [fixedsize=true label="a"]
"a" -> "b"
"b" -> "c"
b [fixedsize=true label="b"]
c [fixedsize=true label="c"]
d [fixedsize=true label="d"]
"f" -> "g"
"c" -> "h"
"g" -> "h"
"main" -> "c" [style=dashed arrowhead=normal]
"origin/main" -> "c" [style=dashed arrowhead=normal]
main [shape=box width=0.8 style=filled fillcolor=lightblue]
"origin/main" [shape=box width=1.2 style=filled fillcolor=lightblue]
}
Let us do an example where there is a new file on the remote repository:
$ git pull
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
From github.com:bbrydsoe/testrepo
55f35b9..e6ca68c main -> origin/main
Updating 55f35b9..e6ca68c
Fast-forward
newfile.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 newfile.txt
Here we create a new branch on a remote repository and add a file to it, then push:
Create the branch
bbrydsoe@enterprise-a:~/mytestrepo$ git branch mynewbranch
bbrydsoe@enterprise-a:~/mytestrepo$
Switch to it
bbrydsoe@enterprise-a:~/mytestrepo$ git checkout mynewbranch
Switched to branch 'mynewbranch'
bbrydsoe@enterprise-a:~/mytestrepo$
I then create a file on my new branch. After this, check the status and log
bbrydsoe@enterprise-a:~/mytestrepo$ git status
On branch mynewbranch
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: myfile.txt
no changes added to commit (use "git add" and/or "git commit -a")
bbrydsoe@enterprise-a:~/mytestrepo$ git log --graph --oneline --decorate --all
* accef46 (HEAD -> mynewbranch, main) Updating the file funny.txt
* 43110f8 (origin/main, origin/HEAD) Update funny.txt
* af9e1cb Create Weird.txt
* db2e945 Create funny.txt
* c74b74c Adding the file file4.txt
* 805d9b3 Create newfile.txt
* c8e5214 Adding some files to play with for the exercise.
* 1c8156d (origin/mytestbranch, mytestbranch) Adding testfile.txt to mytestbranch
* 333d5e5 Merge pull request #2 from bbrydsoe/birgittesbranch
|\
| * 92901fa (origin/birgittesbranch, birgittesbranch) Adding my new file to my new branch.
* | e7741d8 Merge pull request #1 from bbrydsoe/spocksbranch
|\ \
| |/
|/|
| * 6814eb4 (origin/spocksbranch) Added a file by user spock
|/
* d4a666e Adding my new file to the repository.
* 303bf63 Create README.md
bbrydsoe@enterprise-a:~/mytestrepo$
I stage and commit the file.
bbrydsoe@enterprise-a:~/mytestrepo$ git add myfile.txt
bbrydsoe@enterprise-a:~/mytestrepo$ git commit -m " Adding a file to my new branch"
[mynewbranch c997da0] Adding a file to my new branch
1 file changed, 1 insertion(+)
I will check to confirm I am on the right branch:
bbrydsoe@enterprise-a:~/mytestrepo$ git branch
birgittesbranch
main
* mynewbranch
mytestbranch
bbrydsoe@enterprise-a:~/mytestrepo$
Let us again check with git status
and git log ...
bbrydsoe@enterprise-a:~/mytestrepo$ git status
On branch mynewbranch
nothing to commit, working tree clean
bbrydsoe@enterprise-a:~/mytestrepo$
bbrydsoe@enterprise-a:~/mytestrepo$ git log --graph --oneline --decorate --all
* c997da0 (HEAD -> mynewbranch) Adding a file to my new branch
* accef46 (main) Updating the file funny.txt
* 43110f8 (origin/main, origin/HEAD) Update funny.txt
* af9e1cb Create Weird.txt
* db2e945 Create funny.txt
* c74b74c Adding the file file4.txt
* 805d9b3 Create newfile.txt
* c8e5214 Adding some files to play with for the exercise.
* 1c8156d (origin/mytestbranch, mytestbranch) Adding testfile.txt to mytestbranch
* 333d5e5 Merge pull request #2 from bbrydsoe/birgittesbranch
|\
| * 92901fa (origin/birgittesbranch, birgittesbranch) Adding my new file to my new branch.
* | e7741d8 Merge pull request #1 from bbrydsoe/spocksbranch
|\ \
| |/
|/|
| * 6814eb4 (origin/spocksbranch) Added a file by user spock
|/
* d4a666e Adding my new file to the repository.
* 303bf63 Create README.md
bbrydsoe@enterprise-a:~/mytestrepo$
Push your changes with git push origin -u yourbranchname
(or with git push -u origin HEAD
)
bbrydsoe@enterprise-a:~/mytestrepo$ git push -u origin HEAD
Enter passphrase for key '/home/bbrydsoe/.ssh/id_rsa':
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 4 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 619 bytes | 619.00 KiB/s, done.
Total 6 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 1 local object.
remote:
remote: Create a pull request for 'mynewbranch' on GitHub by visiting:
remote: https://github.com/bbrydsoe/mytestrepo/pull/new/mynewbranch
remote:
To github.com:bbrydsoe/mytestrepo.git
* [new branch] HEAD -> mynewbranch
Branch 'mynewbranch' set up to track remote branch 'mynewbranch' from 'origin'.
bbrydsoe@enterprise-a:~/mytestrepo$
Let us check the status
bbrydsoe@enterprise-a:~/mytestrepo$ git status
On branch mynewbranch
Your branch is up-to-date with 'origin/mynewbranch'.
nothing to commit, working tree clean
bbrydsoe@enterprise-a:~/mytestrepo$
We will merge the branches from the command line. Let us first see which branches exist in my repo - in this case I will check for both local and remote branches
bbrydsoe@enterprise-a:~/mytestrepo$ git branch --all
birgittesbranch
main
* mynewbranch
mytestbranch
remotes/origin/HEAD -> origin/main
remotes/origin/birgittesbranch
remotes/origin/main
remotes/origin/mynewbranch
remotes/origin/mytestbranch
remotes/origin/spocksbranch
I have a bunch of branches (created previously by me and another user, for testing). As you can see, I am on my newest branch "mynewbranch"
Switch to the branch we are merging to (main)
bbrydsoe@enterprise-a:~/mytestrepo$ git checkout main
Switched to branch 'main'
Your branch is up-to-date with 'origin/main'.
bbrydsoe@enterprise-a:~/mytestrepo$
Do the merge (it will also open an editor where you should write a commit message for why the merge should happen)
git merge mynewbranch
Merge made by the 'recursive' strategy.
myfile.txt | 1 +
1 file changed, 1 insertion(+)
Let us do a status check
bbrydsoe@enterprise-a:~/mytestrepo$ git status
On branch main
Your branch is ahead of 'origin/main' by 2 commits.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
Pushing
bbrydsoe@enterprise-a:~/mytestrepo$ git push
Enter passphrase for key '/home/bbrydsoe/.ssh/id_rsa':
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 303 bytes | 303.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:bbrydsoe/mytestrepo.git
5fd2960..2032676 main -> main
Checking status again
bbrydsoe@enterprise-a:~/mytestrepo$ git status
On branch main
Your branch is up-to-date with 'origin/main'.
nothing to commit, working tree clean
In this exercise you create SSH keys and upload to GitHub. Then test that it works.
Create a new SSH key
Open a terminal (Git Bash on Windows). In the command below, "GitHub" is a label added to the key for clarity. You can add any you want:
a. Do this
$ ssh-keygen -t ed25519 -C "GitHub"
b. If you have an older system, this may work better
$ ssh-keygen -t rsa -b 4096 -C "GitHub"
You will be asked for a file to save the key. Unless you have an existing SSH key, accept the default.
Enter a passphrase and repeat it.
$ eval "$(ssh-agent -s)"
$ ssh-add ~/.ssh/id_rsa
.ssh
folder, open the file id_rsa.pub
and copy it. Do NOT add any newlines or whitespace!Adding the SSH key to GitHub
Testing the SSH keys
$ ssh -T git@github.com
$ ssh -T git@github.com
The authenticity of host 'github.com (140.82.121.4)' can't be established.
RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,140.82.121.4' (RSA) to the list of known hosts.
Enter passphrase for key '/home/bbrydsoe/.ssh/id_rsa':
Hi bbrydsoe! You've successfully authenticated, but GitHub does not provide shell access.
We now have SSH keys set up. Time to test it from your own machine:
git pull
and see that it works. You will have to enter the key passphrase.git add
, git commit
)ssh-add
to add the key. Then you will only be asked for the passphrase once per session. This is relatively safe on Linux and macOS, but not on Windows where it usually saves the key passphrase permanently.One of you should create a repository on GitHub and invite their team. Remember, on the GitHub webpage the option to create a new repository is in the top right corner - click the "+". To add members: "Settings" -> "Manage access".
git status
. Do a git log --graph --oneline --decorate --all
git pull
before you stage and commit your file and also the team members should use different names for their files. See the changes appear when you do a git pull
after all have added their file(s).git pull --rebase
before you re-do git push
git status
and git log --graph --oneline --decorate --all
before and after each step. Push the files to the repository. Check the log and status again.git branch yourbranchname
where you put any name you want for the new branch.git checkout yourbranchname
git log
and git status
to see any changes.git log
and git status
git push origin -u yourbranchname
(or with git push -u origin HEAD
for a fast way when using the same name)git pull
git status
, git branch
, and git log
to see what has happened.git pull
(on the command line)git status
, git branch --all
, and git log --graph --oneline --decorate --all
to see what has happened.Note: It is possible to make the main branch "protected" so it is not changed without a review from the owner. Try doing this (on GitHub).
git branch -r
git branch
git status
to see which branch you are on.git branch -a
to see all local and remote branchesgit pull
from the command line to get a list of all branches. Switch to the branch you created on GitHub with git checkout --track origin/mynewbranch
. Again do git branch
to see which branch you are on.git status
, git log
). Push the changes.git pull
(on the command line)git status
, git branch
, and git log
to see what has happened. If you want a "prettier" and sometimes easier to read view, use git log --graph --oneline --decorate --all
git branch -D <alsomyownbranch
>git status
, git log
and git branch
to see what has happenedgit pull
and git fetch
, possibly with suitable flags will get it back for you)In this exercise everyone in the team will be working in the same branch, for instance the main branch.
Merge conflicts generally happen when two (or more) teammembers edit the same file and the same line, or when one edits a file and another deletes it.
git pull --rebase
before pushing.git pull
git status
, git branch
, and git log
to see what has happened. Try to resolve the conflict.git rm file
). What happens when you push your work? You should get a conflict. Try and resolve the conflict. Should the file be kept or deleted?