This is a practical workshop consisting of common Git-related actions.
It is based on the unikraft/catalog-core repository, giving us a concrete Git repository to screw up ... hmmmm ... to do wonderful amazing great things to.
First of all, clone the repository:
git clone https://github.com/rosedu/workshop-git
cd workshop-git/And let's get going! 🚀
Note
If, at any point in time, you miss a command, or something bad simply happened, reset the environment by running:
./reset-all.shImportant
We re recommend you write all commands below by hand, i.e. without using copy & paste. This will get you better accustomed to Git and Git commands.
-
See the current branch and status:
git statusGit commands happen in a repository. A repository is the collection of data and metadata that you use to manage content, typically code.
All Git commands start with
git. They are then followed by arguments. Thestatusargument is the argument of choice to get the state of the repository and the current branch.git statusis probably the most used Git command, due to it providing information on the repository state. -
See local branches:
git branchA branch is a specific setup and evolution of the repository. A repository consists of multiple branches, among which you can switch.
-
See local and remote branches:
git branch -aA local repository is typically a clone of a remote repository. It can also be connected to multiple remote repositories. All repositories have branches, that may or may not be in sync with each other.
-
Make remote branches available locally:
git branch base origin/base git branch scripts origin/scripts
A local branch can be created to "follow" a remote branch. Note that changes to local branches are not implicitly made available to remote branches or viceversa. You must use specific actions to sync them:
pushandpull. We will not cover branch syncing in this workshop. -
See local branches again. Note the difference (green color,
*- star character) between the current branch and other local branches:git branch -
Show verbose information about branches:
git branch -v git branch -vv git branch -vv -a
-
Check out to a local branch:
git checkout base git branch git status ls git checkout scripts git branch git status ls git checkout main git branch git status ls
Switching among branches is also called checking out.
-
List contents of another branch without checking out:
git ls-tree scripts git ls-tree scripts:c-fs git ls-tree scripts:c-fs/rootfs git ls-tree scripts --name-only git ls-tree scripts:c-fs --name-only git ls-tree scripts:c-fs/rootfs --name-only git ls-tree -r scripts --name-only
The construct
scripts:c-fsmeans thec-fsfilesystem entry in thescriptsbranch. Similarly, the constructscripts:c-fs/rootfsmeans thec-fs/rootfsfilesystem entry in thescriptsbranch. -
Show contents of a file on another branch without checking out:
git show scripts:c-fs/build.qemu.x86_64 git show scripts:c-fs/README.scripts.md
-
Do more listing. Try out the other commands here.
-
Check out (switch to) the
testbranch from the remoteorigin/testbranch. List contents, get back to themainbranch.
-
Make sure you are on the
mainbranch:git checkout main git status
-
List commit history:
git log git log base git log --oneline base git log --pretty=fuller
A commit is an individual collection of changes to the repository. It represents a given state of the repo. You can compare a commit to a save file or checkpoint in a video game.
A commit consists of actual changes to the files in the repository and metadata: timestamp, authorship, commit description. Commit description is very important to have a good understanding of the repository and rationale for changes. Be sure to follow best practices in creating commits and commit descriptions.
A branch is a series of commits, called a history of commits. Each branch has its own commit history.
-
Show top commit contents:
git show git show HEAD
The
HEADkeyword is an alias for the latest (most recent) commit in the current branch. -
Show commit contents by commit ID:
git show 1a02ae3Each commit is identified by a commit ID, typically a SHA-1 hash of the commit contents.
-
Only show actual contents, no metadata:
git show --pretty="" 1a02ae3 -
Only show modified files:
git show --pretty="" --name-only 1a02ae3 -
Show commits that modified a file:
git checkout base git blame README.md git checkout main
git blameshows the latest modification of each line in the file: latest commit ID, together with timestamp and author. -
Show commits that modified a path:
git log scripts -- README.md git log scripts -- c-fs
-
Show difference between two commits / references:
git diff base scriptsgit diffshows lines that need to be added (+) or removed (-) in order to get from the first reference to the second one. -
Show commit difference between two references:
git cherry -v main scripts git cherry -v scripts main git cherry -v main d379011 git cherry -v d379011 main
-
How are the three branches (
base,scripts,test) constructed? Which is constructed on top of the other? -
Do more history inspection.
-
Show local Git configuration:
git config -l git config user.name git config user.emailGit uses configuration variables, such as
user.nameoruser.emailorcolor.uito customize command effects, i.e. author being used, coloring, aliases, files to be ignored. -
If not already configured, configure your name and e-mail:
git config --global user.name "<your-full-name-here>" git config --global user.email "<your-email-here>"
Use your full name as you would typically do:
Firstname Lastname. -
Configure colored output:
git config --global color.ui "auto" -
Configure aliases:
git config --global alias.lg 'log --pretty=fuller' git lg
An alias is typically a short name for a common command, to make typing more efficient or to make it easier to remember.
-
Check the configuration:
cat .git/config cat ~/.gitconfig
The
.git/configis the local repository Git configuration file. The~/.gitconfigis the global Git configuration file. Thegit configcommand reads from and makes updates to these files.
We make mistakes:
- We create commits we shouldn't have (yet) created.
- We leave changes out of commits.
- We amend the wrong commit.
- We do merges / rebases / cherry-picks that fail.
- We add a change in the staging area that we shouldn't have (yet) added.
- We delete a file by mistake.
These things happen. We solve them. We just need to know how to do that.
-
First prepare a messed up environment, by running:
./mess-it-up.sh -
Now look at the mess:
git status -
See the commit history:
git logSee the
bla blacommit (latest). And see the wrong message (Bueinstead ofBye) for the other other commit. -
Note: If, at any point in time, you miss a command, or something bad simply happened, reset the environment by running:
./reset-all.shThe go back to step 1 and prepare the messed up environment again.
-
Let's first get rid of all the temporary files we do not require (they were added by mistake):
git clean -d -f git status
git cleanremoves files that are not used (not tracked) in the repository. Files are tracked if there are commits with contents of those files. Files are not tracked / untracked if there are no commits related to those files.Untracked files appear, in the
git statusoutput, typically colored red, marked withUntracked files:. -
Restore the deleted
Makefile:git status git restore c-hello/Makefile git status
When you delete or modify a tracked file, its changes appear in the "tracking" area, typically colored red, marked with
Changes not staged for commit:. You can restore changes to its original contents. -
Restore the staged deleted
hello.c:git status git restore --staged c-hello/hello.c git status git restore c-hello/hello.c git status
When you delete or modify a tracked file, and then you add changes (via
git addorgit rm), its changes appear in the "staging" area, typically colored green, marked withChanges to be committed:. You can get a file from the "staging" area to the "tracking" area viagit restore --staged. You can then restore changes to its original contents, and remove the file from the "tracking" area, viagit restore. -
Restore the staged new file
c-bye/build.fc.x86_64to create a proper commit:git status git restore --staged c-bye/build.fc.x86_64 git status
-
See the commit history again:
git log -
Remove the
bla blacommit:git reset HEAD^ git status git log rm blabla.txt
Adding a caret sign (
^) after a commit reference points to the previous commit.git resetdiscards all commits after the argument reference. -
Update the commit message from
BuetoBye:git log git commit --amend # do edit as required git log git show
The
git commit --amendcommand presents a commit description edit screen. After saving the changes and exiting the edit screen, the commit description (and its ID / SHA hash) are updated. -
Create a new commit with the
c-bye/build.fc.x86_64file:git add c-bye/build.fc.x86_64 git commit -s -m 'c-bye: Add Firecracker build script for x86_64' git log git show git status
-
Repeat the above steps at least 2 more times.
Aim to have one time without checking the instructions. That is, run the
./mess-it-up.shscript and then repair the mess by yourself.If, at any point, you get lost, run the reset script:
./reset-all.sh -
Do your own messing up of the environment. Go to a given branch, remove files, create new files, create commits, add files to staging area etc. Then repair the environment.
If, at any point, you get lost, run the reset script:
./reset-all.sh
Let's get to a situation where we need to repair the commit history. We will have a setup where we have the following stack of commits:
- (top) correct commit
- (next) commit that shouldn't exist (
bla blacommit) - (next-next) commit with a typo (
Bueinstead ofBye)
We want to edit the commit history and:
- Remove the
bla blacommit. - Fix the typo.
-
Create the setup:
./set-up-history-edit.sh git status git log
-
Note: If, at any point in time, you miss a command, or something bad simply happened, reset the environment by running:
./reset-all.shThe go back to step 1 and prepare the messed up environment again.
-
Go into commit history editing mode:
git rebase -i HEAD~3The
rebasecommand positions you somewhere else in the commit history. You can make updates to commits from that point onward.The
~Nconstruct is a reference toNcommits before the current one.HEAD~3means 3 commits before the top commit.You get an editor screen with an output like this:
pick e5442f0 Add C Bue application pick 06eb1fa bla bla pick 74f3d3e c-bye: Add Firecracker build script for x86_64 -
Edit the rebase screen contents in order to edit the commit with the typo (
Bueinstead ofBye) and to drop the extra commit (the one withbla bla). Have the editor screen have the contents:edit e5442f0 Add C Bue application drop 06eb1fa bla bla pick 74f3d3e c-bye: Add Firecracker build script for x86_64That is, the first line (the bad commit message) should have
editinstead ofpick- we edit the commit. And the second line (the extra commit) should havedropinstead ofpick- we drop the commit.Save and exit the editor screen.
-
We are currently editing the typo commit:
git log git show
-
Update the commit message from
BuetoBye:git log git commit --amend # do edit as required git log git show
-
Continue the commit history editing:
git rebase --continueEach
git rebase --continuecommand gets you to the next commit to update. -
The extra commit has been dropped:
git log git status
-
The commit history editing (aka the rebase) is done:
git rebase --continueIt says "No rebase in progress?", meaning the rebase is done. There are no more commits to update.
-
Repeat the above steps at least 2 more times.
Aim to have one time without checking the instructions. That is, run the
./set-up-history-edit.shscript and then repair the commit history by yourself.If, at any point, you get lost, run the reset script:
./reset-all.sh -
Do your own commit history that you want to edit. Go to a given branch, create commits, create some bad or extra commits. Then repair the commit history.
If, at any point, you get lost, run the reset script:
./reset-all.sh
We will get some new content that we will add as commits to the repository.
Make sure you are on the main branch and everything is clean:
git checkout mainOr, reset the repository:
./reset-all.sh-
Create contents from archive:
unzip support/c-bye.zip git status
We now have a
c-bye/directory:ls c-bye/There are a lot of files. We want to add them as 3 separate commits in 3 separate branches.
- The
bye.c,Makefile,Makefile.uk,fc...,xen...,README.mdfiles will go to thebasebranch. - The
defconfig...,build...,run...,README.scripts.mdfiles will go to thescriptsbranch. - The
test...files will go to thetestbranch.
- The
-
Go to the
c-bye/directory:cd c-bye/ ls
Let's create commit to base branch:
-
Check out the
basebranch:git checkout base -
Add contents to staged area:
git add bye.c Makefile Makefile.uk README.md xen.* fc.* .gitignore git status
-
Create the commit:
git commit -s -m 'Introduce C Bye on Unikraft' -
List the commit:
git log git show
-
Look at the commit for the initial C Hello program:
git show 7fd6e9290dddc2ae799ae5df684668a7d16e87f3The commit message is:
Introduce C Hello on Unikraft Add `c-hello/` directory: - `hello.c`: the source code file - `Makefile.uk`: defining the source code files used - `Makefile`: for building the application - `fc.x86_64.json` / `fc.arm64.json`: Firecracker configuration - `xen.x86_64.cfg` / `xen.arm64.cfg`: Xen configuration - `README.md`: instructions - `.gitignore`: ignore generated filesWe want something similar for our C bye commit.
-
Update the commit message by amending:
git commit --amendYou get an editable screen. Edit the commit message to have contents similar to the one for C Hello. Use copy & paste.
You can use
git commit --amendto constantly update the commit.See the final result with:
git log git show
Let's create commit to scripts branch:
-
Check out the
scriptsbranch:git checkout scripts -
Add contents to staged area:
git add defconfig.* build.* run.* README.scripts.md git status
-
Create the commit:
git commit -s -m 'c-bye: Add scripts' -
List the commit:
git log git show
-
Look at the commit for the initial C Hello program:
git show 04cf0f57f2853d73a5c25082d4652fef63da8f57The commit message is:
c-hello: Add scripts Use scripts as quick actions for building and running C Hello on Unikraft. - `defconfig.<plat>.<arch>`: default configs, used by build scripts - `build.<plat>.<arch>`: scripts for building Unikraft images - `run.<plat>.<arch>`: scripts for running Unikraft images - `README.script.md`: companion README with instructionsWe want something similar for our C bye commit.
-
Update the commit message by amending:
git commit --amendYou get an editable screen. Edit the commit message to have contents similar to the one for C Hello. Use copy & paste.
You can use
git commit --amendto constantly update the commit.See the final result with:
git log git show
Let's create commit to test branch:
-
Check out the
testbranch:git checkout test -
Add contents to staged area:
git add test* git status
-
Create the commit:
git commit -s -m 'c-bye: Add test scripts' -
List the commit:
git log git show
-
Reset the configuration:
./reset-all.sh -
Repeat the above steps at least 2 more times.
Aim to have one time without checking the instructions. That is, create the 3 commits in the 3 branches for C bye.
If, at any point, you get lost, run the reset script:
./reset-all.sh -
Do the same for the C++ Bye program.
Make sure you are on the
mainbranch:git status git checkout main
First unpack the contents:
unzip support/cpp-bye.zip git status
We now have a
cpp-bye/directory:ls cpp-bye/There are a lot of files. We want to add them as 3 separate commits in 3 separate branches.
- The
bye.cpp,Makefile,Makefile.uk,Config.uk,fc...,xen...,README.mdfiles will go to thebasebranch. - The
defconfig...,build...,run...,README.scripts.mdfiles will go to thescriptsbranch. - The
test...files will go to thetestbranch.
Follow the steps for C Bye to create the commits for C++ Bye.
- The
-
Do the same for the Python Bye program.
Make sure you are on the
mainbranch:git status git checkout main
First unpack the contents:
unzip support/python3-bye.zip git status
We now have a
python3-bye/directory:ls python3-bye/There are a lot of files. We want to add them as 3 separate commits in 3 separate branches.
- The
bye.py,Makefile,Makefile.uk,Config.uk,fc...,xen...,README.mdfiles will go to thebasebranch. - The
defconfig...,build...,run...,README.scripts.mdfiles will go to thescriptsbranch. - The
test...files will go to thetestbranch.
Follow the steps for C bye to create the commits for Python3 Bye.
- The
At this point there are commits in the base branch that are not part of the scripts branch.
And there are commits in the scripts branch that are not part of the test branch.
We aim to have the scripts branch built on top of the base branch.
And we want to have the test branch built on top of the scripts branch.
For this, all commits from the base branch will have to be on the scripts branch.
And all commits from the scripts branch will have to be on the test branch.
-
To get the
c-byecommit from thebasebranch to thescriptsbranch, first check out thescriptsbranch:git checkout scripts -
Find out the commit ID in the
basebranch:git log baseScroll the commit history and copy the commit ID belonging to the
c-byeprogram. That is, the ID of the commit you created earlier with the message:Introduce C Bye on Unikraft. -
Use the commit ID cherry pick the commit from the
basebranch to thescriptsbranch:git cherry-pick <commit-id>Replace
<commit-id>with the commit ID you copied above (copy & paste). -
Check the updated history of the
scriptsbranch:git log -
To get the
c-byecommits from thescriptsbranch to thetestbranch, first check out thetestbranch:git checkout test -
First cherry pick the
basecommit that is now onscripts:git cherry-pick <commit-id>This is the same commit ID from above.
Cherry-picking is selecting a commit, or series of commits and adding them on top of the current setup.
-
Now let's get the
c-byecommit from thescriptsbranch. Find out the commit ID in thescriptsbranch:git log scriptsScroll the commit history and copy the commit ID belonging to the
c-byeprogram. That is, the ID of the commit you created earlier with the message:c-hello: Add scripts. -
Use the commit ID cherry pick the commit from the
scriptsbranch to thetestbranch:git cherry-pick <new-commit-id>Replace
<new-commit-id>with the commit ID you copied above (copy & paste). -
Check the updated history of the
scriptsbranch:git log
Note
If, at any point, you did something wrong, recall that you can drop the top commit by doing:
git reset --hard HEAD^-
Repeat the above steps at least 2 more times.
Aim to have one time without checking the instructions. That is, have the
scriptsbased on thebasebranch, and have thetestbranch based on thescriptsbranch.For starters, drop the newly cherry-picked commit from the
scriptsbranch:git checkout scripts git reset --hard HEAD^
And drop the newly cherry-picked commits from the
testbranch:git checkout test git reset --hard HEAD^^
Now repeat the steps above.
-
Do the same steps for the C++ Bye program. That is, have the
scriptsbased on thebasebranch, and have thetestbranch based on thescriptsbranch. -
Do the same steps for the Python Bye program. That is, have the
scriptsbased on thebasebranch, and have thetestbranch based on thescriptsbranch.
At this point, the scripts branch is based on the base branch.
And the test branch is based on the scripts branch.
What we do not like, however, is that the commits in the scripts and the test branch are not in the correct order.
In the scripts branch the commits are (top-to-bottom):
- python3-bye: Add scripts
- Introduce Python3 Bye
- cpp-bye: Add scripts
- Introduce C++ Bye
- c-bye: Add scripts
- Introduce C Bye
- python3-bye: Add test scripts
- cpp-bye: Add test scripts
- c-bye: Add test scripts
Use git log to confirm this:
git log
git log --onelineThe order we want is (top-to-bottom):
- python3-bye: Add test scripts
- python3-bye: Add scripts
- Introduce Python3 Bye
- cpp-bye: Add test scripts
- cpp-bye: Add scripts
- Introduce C++ Bye
- c-bye: Add scripts
- Introduce C Bye
- c-bye: Add test scripts
So, to update the commit history, follow the steps below:
-
Enter the history update mode. Update the last
9commits:git rebase -i HEAD~9You are now in a custom editor mode where you can update the commits.
-
Move the commits (cut & paste) to get to the new commit history. Do this by cutting and pasting the lines in the commit history.
-
Save the custom editor mode.
You're done. Check the new commit history with:
git log
git log --onelineEdit the commit history on the test branch are in the correct order, the same they are now on the scripts branch.