README
This is a small repository to train version controlling with git.
During this training, you will practise how to utilize standard git commands such as git commit
, git push
, git merge
, git cherry-pick
and more.
Thereby, yu will also become familar with the git-rules defined in our ESDE-group (see this presentation.)
Software requirements
- Python 3
- packages: typing, argparse
Getting started
- Team up with another group member and sit next to each other or open a video call.
-
One team member only: Create a fork from the landing page of this repository.
Please fork it to your personal namespace.
- As of version 15, gitlab does not support the creation of merge requests in forked repositories when creating a new issue (see here). Since we would like to use this feature and since we are typically not forking from ESDE-internal git repositories, we are going to destroy the fork relationship explicitly. To do so, select
Settings > General
on the left side bar and expandAdvanced
. Scroll down to theRemove relationship
-section, selectRemove fork relationship
and confirm. - Hover over
Manage
in the left menu bar and click onMembers
to add your partner to the (private) fork. Assgn him/her the role as a Developer. -
Both: Clone the repository either on your local machine or to a proper directory on the JSC HPC-system.
git clone <url>
You can obtain the url as shown below. - If you are working on the HPC-system, load the Python module.
ml Python
Ensure that you have Python3 available when you work on local system (e.g. in a conda environment)
Exercises
Task 1
One team member: Set up a dev
-branch (starting from the main
-branch) from which all feature-branches will be created subsequently. Change to the dev
-branch and push the newly created branch to the remote repository.
Solution
Run the following commands in your console:git branch dev
git checkout dev
# short-cut: git checkout -b dev
git push -u origin dev
The other team member: Fetch and switch to the dev
-branch once it is available in the remote repository.
Solution
Run the following commands in your console:git fetch
git checkout --track origin/dev
Task 2
Go to the git-repository in your web-browser and add two issues - one for implementing the add
-method and one for the multiply
-method in calculator.py
. Each team member should be assigned to one of the issue tasks and is therefore responsible to write a brief description and to create the corresponding issue/feature-branch as well as the merge-request.
Note: Choose the dev
-branch as your source and follow the ESDE naming convention when creating the merge-request.
Solution
After creating the issue, click on the `Create merge request`-button. The source and the new issue-branch can be created as follows:
Task 3
Change to the newly created feature-branch in your working environment's console/IDE and implement the method that you are responsible for in calculator.py
. Afterwards add and commit your changes.
Note: Just change adapt return
-statement of your method. We will implement more sophisticated versions of the methods later.
Solution for add-method
First, change to the feature-branch:git fetch
git checkout --track origin/{your_add_branch}
def add(a: Union[int, float], b: Union[int, float]) -> Union[int, float]:
"""
Add numbers and return sum.
:param a: first number
:param b: second number
:return: sum of a and b
"""
return a + b
git add calculator.py
git commit -m "Implement add-method"
Solution for multiply-method
First, change to the feature-branch:git fetch
git checkout --track origin/{your_add_branch}
def multiply(a: Union[int, float], b: Union[int, float]) -> Union[int, float]:
"""
Multiply numbers and return product.
:param a: first number
:param b: second number
:return: product of a and b
"""
return a * b
git add calculator.py
git commit -m "Implement multiply-method"
Task 4
Revise the previously created commit to check your statement. Correct it if you find typos or if you would like to provide a better one.
Check the functionality of your method by running main.py
and push your changes to the remote repository afterwards. For the add
-method, run the following:
python3 main.py -op add
Adapt the -op
-argument to test the multiply
-method.
Note: Continue to test your implementations in the subsequent tasks.
Solution
The following commands should be run:git commit --amend # adapt commit in text editor and save and quit
git push
Task 5
Set-up two new feature branches (one for each method) to make the methods in calculator.py
more robust. Therein, implement checks that the parsed arguments are numbers, i.e. integeres or floats (check the solution for the appropriate lines of code). Proceed as described in Task 2 when setting up the branches. Finalize the modifications by committing and pushing.
Solution
The steps are analogous to the solution of Task 2 and 3. The following lines should be added to the `add`- and `multiply`-method in `calculator.py`, respectively.if not (isinstance(a, (int, float)) and isinstance(b, (int, float))):
raise ValueError("a and b must be numbers")
Afterwards, the revised code can be commited and pushed. For the `add`-method, the following commands are suitable.
git add calculator.py
git commit -m "Implemented check for parsed arguments in add-method."
git push
The commands for the `multiply`-method are analogous.
Task 6
Exchange the revised methods using git cherry-pick <hash>
. Ensure that the modifications to the methods in the feature branches are made available via pushing and fetching beforehand.
Tip: The commit-hash can be obtained from both, the gitlab's web editor and from the command line.
Solution
The required commit hash can be conviently retrieved from the commit-history of the respective feature branch. This can be assessed via the web editor, by clicking onCommits
under theCode
-section of the left side bar and by choosing the respective feature-branch. Alternatively, one may also get the hash via the command line:
git checkout {other_feature_branch} # change branch
git log # check commit hashes
git checkout {my_feature_branch} # only required if you have changed it to get the hash
git cherry-pick {hash}
Task 7
In this task, both team members will work on the add
-method to enable handling of more than two parsing arguments. Each team member creates an individual feature branch (analogous to Task 2, 3 and 5) first. Afterwards, one team member will realize a rather inflexible approach by adding optional keyword arguments (i.e. adding third = None, fourth = None
to the signature of the add
-method), the second team member will apply Python's *args
-variable to pass a varying number of positional arguments (see this post for more information on args
and kwargs
). After committing and pushing, the feature should be merged into each other with git merge --no-ff {other_feature_branch}
to provoke a merge conflict. Finalize the task by solving the merge conflict.
Solution
The inflexible approach with additional keyword arguments may look as follows:def add(a: Union[int, float], b: Union[int, float], c: Union[int, float]= None, d: Union[int, float] = None) -> Union[int, float]:
"""
Add numbers and return sum.
:param a: first number
:param b: second number
:param c: third number (optional)
:param d: fourth number (optional)
:return: Sum of the numbers.
"""
# check if a and b are numbers
if not (isinstance(a, (int, float)) and isinstance(b, (int, float))):
raise ValueError("a and b must be numbers")
result = a + b
if c is not None:
if not isinstance(c, (int, float)):
raise ValueError("c must be a number")
result += c
if d is not None:
if not isinstance(d, (int, float)):
raise ValueError("d must be a number")
result += d
return result
*args
is implemented with the following code lines:
def add(*numbers: Union[int, float]) -> Union[int, float]:
"""
Add numbers and return sum.
:param numbers: Arbitrary number of numbers to be added. At least two numbers must be provided.
:return: Sum of the numbers.
"""
# check if at least two numbers are provided
if len(numbers) < 2:
raise ValueError("At least two numbers must be provided.")
# get sum while checking if all numbers are numbers
result = 0
for number in numbers:
if not isinstance(number, (int, float)):
raise ValueError(f"All numbers must be numbers, but got {number} (type: {type(number)}) instead.")
result += number
return result
The adapted methods are then added, commited and finally pushed to the remote repository.
git add calculator.py
git commit -m "More arguments for add-method"
git push
Merging the partner's feature branch then yields a merge-conflict
where the conflicting code parts are separated as follows:
<<<<<<< HEAD
from typing import Union
def add(*numbers: Union[int, float]) -> Union[int, float]:
"""
Add numbers and return sum.
:param numbers: Arbitrary number of numbers to be added. At least two numbers must be provided.
=======
def add(a: Union[int, float], b: Union[int, float], c: Union[int, float]= None, d: Union[int, float] = None) -> Union[int, float]:
"""
Add numbers and return sum.
:param a: first number
:param b: second number
:param c: third number (optional)
:param d: fourth number (optional)
>>>>>>> michael_issue004_multiple_args_forr_add
:return: Sum of the numbers.
[...]
Removing the code parts of the inflexible approach (incl. the separators) followed by
git commit
git push
then solves the merge conflict and pushes the final version to the remote repository.
Task 8
One team member only: Merge the recent feature-branch into the dev
-branch.
Solution
Merging is realized as follows:git checkout dev # switch to dev-branch
git merge --no-ff {feature_branch_from_task7}
Task 9
In this task, we will mock a situation where stashing is useful. First, switch to the dev
-branch and enable handling of flexible arguments for the multiply
-method. Since new features shouldnot be implemented directly on this branch, we stash our modifications to calculator.py
and create the respective merge request afterwards (incl. the corresponding issue). Make the stashed modification available after switching to the newly created feature branch and finalize the work by adding, committing and pushing to the remote repository. One team member may also merge the feature into the dev
-branch.
Solution
After switching to thedev
-branch via
git checkout dev
multiply
-method may look as follows:
def product(*numbers: Union[int, float]) -> Union[int, float]:
"""
Multiply numbers and return product.
:param a: first number
:param b: second number
:return: product of a and b
"""
# check if at least two numbers are provided
if len(numbers) < 2:
raise ValueError("At least two numbers must be provided.")
# get product while checking if all numbers are numbers
result = 1
for number in numbers:
if not isinstance(number, (int, float)):
raise ValueError(f"All numbers must be numbers, but got {number} (type: {type(number)}) instead.")
result *= number
return result
git stash
git checkout {feature_branch_task9} # change to new feature branch
git stash pop
git add calculator.py
git commit -m "Flexible number of arguments for multiply-method"
git push
The final merge to the dev-branch is then realized by
git checkout dev
git merge --no-ff {feature_branch_task9}
Task 10
The final task deals with undoing a coding error that has already been commited.
To do so, modify and commit calculator.py
in a way that it breaks the code (e.g. by an indentation error or by a typo). Then correct the code using git reset
in an appropriate way.
Solution
After corrupting committing the scriptgit add calculator.py
git commit -m "Faulty change to calculator.py"
git log
to obtain the commit-hash of the last correct code-version which we would like to reconstruct. Removing the faulty commit from the git history is then realized via
git reset --hard {commit_good_code}
Congratulations
You have successfully learned how to version control your software developments wit git!