{
"cells": [
{
"cell_type": "markdown",
"id": "6ec03312-2c38-4b82-81a3-e7c6191bd6cd",
"metadata": {
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"# Introduction to code versioning with git\n",
"\n",
""
]
},
{
"cell_type": "markdown",
"id": "12ca5e12-81e5-4223-8008-f5397359c9df",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"## schedule\n",
"\n",
"- introduction to files history
\n",
"- introduction to Git, a system of code versioning
\n",
"- the Git cycle
\n",
"- branches
\n"
]
},
{
"cell_type": "markdown",
"id": "938fbb83-418a-4104-84da-bb6f14dd52fb",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"## support\n",
"\n",
"jupyter notebook running into the `jupyter/minimal-notebook` docker container
\n",
"1. `docker run -it --rm -p 8888:8888 --user root -e NB_USER=\"tutoriel_git\" -e CHOWN_HOME=yes -v \"${PWD}:/home/${NB_USER}\" jupyter/minimal-notebook`
\n",
"2. in a browser, open the last given URL http://127.0.0.1:8888/lab?token=xxx
\n",
"3. cp notebook `tutotiel_git.ipynb` & `images` repository into the `${PWD}/tutotiel_git/` repository
\n",
"\n",
"**note:** \n",
"- notebook with a Python kernel: use `%%sh` for shell (bash) in code cells
\n",
"- docker container: use `cd ${PWD}/xxx/` in code cells to work into `xxx`
\n",
"- at the end of the notebook, the `tutoriel_git` repository will look like:
\n",
"`├── FAIR_bioinfo_github`
\n",
"`│ └── README.md`
\n",
"`├── first_git_example`
\n",
"`│ ├── file1.txt`
\n",
"`│ └── file2.txt`
\n",
"`└── tutoriel_git.ipynb`"
]
},
{
"cell_type": "markdown",
"id": "44fd4ba2-1dda-4bb8-9504-0fd86faf0772",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"## Really need of a files history?\n",
"\n",
"\n",
"\n",
"_Most researchers are primarily collaborating with themselves,” Tracy Teal explains. “So, we teach it from the perspective of being helpful to a ‘future you’.”_"
]
},
{
"cell_type": "markdown",
"id": "d149c568-6f7d-4c71-a58e-5feee5daeb4e",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"## Files history, a good practice for reproducible research\n",
"\n",
"_”Rule 4: Version Control All Custom Scripts”_\n",
"\n",
""
]
},
{
"cell_type": "markdown",
"id": "667ededf-27e9-4f53-8a38-54aa37a2dee2",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"## Code control version\n",
"\n",
"**Definition:** version control, revision control, source control, or source code management: class\n",
"of systems responsible for managing changes to files\n",
"\n",
"**Feature:** each revision is associated with a timestamp and the person making the change. Revisions can be compared, restored, and merged\n",
"\n",
"**Software:** SVN, Git, Mercurial, GNU arch, etc\n",
"\n",
"We choose Git."
]
},
{
"cell_type": "markdown",
"id": "0981a5e1-4112-4a93-8e2c-21e0f286ded3",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"## Git vs. GitHub\n",
"\n",
"\n",
"- will track and version your files
\n",
"- enables you to collaborate with ... **yourself** (and others if they have local access)
\n",
"- open source license GPL (GNU General Public License)
\n",
"- created in 2005 by Linus Torvalds for the development of the Linux kernel \n",
"\n",
"\n",
"- stores online Git repositories
\n",
"- enables you to collaborate with **others** (and **yourself!**)
\n",
"- sources belong to Microsoft
\n",
"- code deposited on it is shared with Microsoft (beware of sensitive data)
\n",
"- first commit in 2007 by Chris Wanstrath"
]
},
{
"cell_type": "markdown",
"id": "04448aa8-0f34-4bf1-85cd-5609785be13f",
"metadata": {
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"## Git concepts, Git objects\n",
"\n",
"- **working directory:** a user private copy of a whole repository of interest
\n",
"- **clone:** a local copy of a repository (with all commits and branches), the original repository can be local, or remote (http access)
\n",
"- **commit:** a git _object_, compressed snapshot of your entire repository; the _command_ that saves changes by creating the snapshot
\n",
"- **HEAD:** pointer to your current working commit. Can be moved (git checkout) to branches, tags, or commits
\n",
"- **branch:** a lightweight movable pointer to a commit
\n",
"- **merge:** combines remote tracking branch into current local branch
\n",
"- **staging area:** list of files of the working directory that will be considered for next commit (ie. could be not all the modified files)
\n",
"- **tag**: a version you want to memorize"
]
},
{
"cell_type": "markdown",
"id": "e6dca274-d8db-4846-9dee-5e94ec9bc16b",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"- **Revision graph:** \n",
""
]
},
{
"cell_type": "markdown",
"id": "bac1a3d9-e312-4785-a9eb-87109685ea56",
"metadata": {
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"## Git setup\n",
"\n",
"**Git configuration:** check the configuration of your git `user.name` with:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bb0ec605-84b0-4d1e-a43c-703ca93fcce8",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"git config --list"
]
},
{
"cell_type": "markdown",
"id": "196ac849-ec8a-4398-a870-9d385e8f6d37",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"if not yet done (nothing displayed), tell git our identity:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4d08cffb-8a30-4f6a-99a8-f6aad9cabf54",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"git config --global user.name ’clairetn’\n",
"git config --global user.email ’claire.ctn@gmail.com’\n",
"git config --list"
]
},
{
"cell_type": "markdown",
"id": "17b823af-6e4b-482f-8e3a-e7f18125aacc",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"**Git repository initialisation:** \n",
"The initialisation (red arrow) is the creation of a `.git` repository:\n",
"\n",
"[](images/FAIR_git_init.png)\n",
"\n",
"3 ways to initialise a git repository:
\n",
"- `git init`: inside an existing folder (possibly containing files)
\n",
"- `git init myproject`: create `myproject` folder + initialize the `.git` subfolder inside it
\n",
"- `git clone /gitfolder/path /new/path`: copy the existing git repository to a new one"
]
},
{
"cell_type": "markdown",
"id": "f22b4bd6-d568-4629-8c59-fb94dc09a7fb",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Initalise a git repository:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "442947ef-c42f-44f0-8505-1d9d175b03d5",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"git init first_git_example"
]
},
{
"cell_type": "markdown",
"id": "c3c60262-6296-4d08-a23d-54849c992c82",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Observe the git folder:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e4353d2c-0cf5-46fb-b6db-5f5c29271e55",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"ls -lah first_git_example"
]
},
{
"cell_type": "markdown",
"id": "42ae9844-7001-41bf-96e9-2b257dd05c73",
"metadata": {
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"## Git work cycle, 3 steps\n",
"\n",
"1. create/delete/change files
\n",
"2. place the files to follow to a special space, the staged area with **`add myfiles`**
\n",
"[](images/FAIR_git_add.png)\n",
"3. keep the actual version of the files included in the staged area with **`commit -m \"my reason of change\"`**
\n",
" [](images/FAIR_git_commit.png)"
]
},
{
"cell_type": "markdown",
"id": "489d221f-4020-4216-908c-f4e08ad66e95",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"The **`status`** command explains the git step of each file of the folder:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f1f7380f-b21c-4615-b62a-def77225060f",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/first_git_example\n",
"git status"
]
},
{
"cell_type": "markdown",
"id": "6bb681e2-2d84-4c84-8d9f-9674fb0247af",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Now, experiment one git cycle. \n",
"\n",
"**Create 2 files**:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "146c0a2f-e57f-4033-a7fd-b647f45f76b4",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/first_git_example\n",
"for i in 1 2 ; do \n",
" echo \"text of file \"${i}\"\\n\" > file${i}.txt ;\n",
"done\n",
"ls"
]
},
{
"cell_type": "markdown",
"id": "09bc48ed-5d25-401a-acec-edb3a05c9afd",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Check the git **status**:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "47960190-1b33-475f-9d33-15774cbe5a9c",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/first_git_example\n",
"git status"
]
},
{
"cell_type": "markdown",
"id": "283c0685-f354-4e1c-8f3c-cd7630f028ab",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"Observe: the 2 new files are included in the list of untracked files."
]
},
{
"cell_type": "markdown",
"id": "09eab47b-84a5-4f9a-8832-62f2042e6222",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Add `file1.txt` to the list of tracked files, the **staged area**:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ff3bb3d7-6709-4706-bb04-ee94472c3a72",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/first_git_example\n",
"git add file1.txt\n",
"git status"
]
},
{
"cell_type": "markdown",
"id": "db2a6237-4e43-49e8-8cc5-7f73d8359850",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"`file1.txt` pass from untracked to staged (ie. to be committed)."
]
},
{
"cell_type": "markdown",
"id": "17fcd7f5-dc48-4c5f-ade1-43cb501189b8",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Change again the content of `file1.txt`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3f1b3a61-d37d-4e85-9fae-9fafa62c49ed",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/first_git_example\n",
"sed 's/text/text change /' file1.txt > tmp ; mv tmp file1.txt\n",
"git status"
]
},
{
"cell_type": "markdown",
"id": "1216bf16-98c3-453b-851c-775541320170",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"observe the 3 states.\n",
"Note that `file1.txt` appears in _to be commited_ and also in _not staged for commit_. Why?"
]
},
{
"cell_type": "markdown",
"id": "4127c898-f66f-48aa-af0f-b8dda57eb5ff",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"**Stage** all files:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "049b9662-ac4d-47e7-b872-ddc323d5a2ae",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/first_git_example\n",
"git add file?.txt\n",
"git status"
]
},
{
"cell_type": "markdown",
"id": "8f165243-0e8b-4866-94b2-d9e4218e73cc",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"And **commit**:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f8d0ff8a-642a-4e55-bf4e-28723e2a1228",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/first_git_example\n",
"git commit -m \"commit with all files\"\n",
"git status"
]
},
{
"cell_type": "markdown",
"id": "6d060b97-c56d-4c56-8b21-4405b9b78024",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"note on commit message (`-m`):
\n",
"follow [convential-commits](https://www.conventionalcommits.org) specification for adding **human** and **machine readable** meaning:\n",
"\n",
" [optional scope]: \n",
" [optional body]\n",
" [optional footer(s)]\n",
" \n",
"type examples: `feat fix build chore ci docs style perf test ...`
\n",
"[here](https://blog.stack-labs.com/code/devops_conventional_changelog/) an example in French"
]
},
{
"cell_type": "markdown",
"id": "2088c2a7-bc3d-4faa-bc6a-c646a283127f",
"metadata": {
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"## middle conclusion\n",
"\n",
"So far, you've started a new project whose code is versioned by git.
\n",
"You have created files and all their successives changes were tracked.\n",
"\n",
"To avoid bad changes of code, it is a good practice to test a new code version before use it, and so separate development code from production code. \n",
"With the Git **branch** concept, you may manage this separation: develop code from an initial copy of the master code."
]
},
{
"cell_type": "markdown",
"id": "60697aa1-1c3d-4a41-849e-032da84d01bc",
"metadata": {
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"## Use branches\n",
"\n",
"We will now create a 2nd project by copying an already existing one (from an online git project site, _e.g._ github):"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4c15fa65-d8d5-45aa-83f6-b6b60488a250",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"git clone https://github.com/clairetn/FAIR_bioinfo_github.git\n",
"ls -lah FAIR_bioinfo_github/ "
]
},
{
"cell_type": "markdown",
"id": "7d543d43-6a0f-45b2-84bf-efb03869aeed",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"Observe the result:\n",
"- a new folder has been created\n",
"- its name is directly deduced from the URL\n",
"- it contains a `.git` repository and a `README.md` file: it is a minimal project!"
]
},
{
"cell_type": "markdown",
"id": "36fb0303-c49b-4e16-84b5-97de23f6ea16",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"To developpe a new functionality, add a branch with **`branch`**:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e31344b3-c4be-4dec-91e1-2c6bb8590357",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/FAIR_bioinfo_github\n",
"git branch branch_myfn # create a branch\n",
"git branch # list all branches"
]
},
{
"cell_type": "markdown",
"id": "c0d7427c-d93a-4eda-a268-c62a1ecdf2fa",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"The default branch is nammed `master`. The star denotes the working branch."
]
},
{
"cell_type": "markdown",
"id": "23e31f8c-da6a-4e50-be1a-faf118f6f738",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Move to the new branch with **`checkout`**:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f5b1c624-51e0-44a3-a5e5-e6f444c4001d",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/FAIR_bioinfo_github\n",
"git checkout branch_myfn\n",
"git branch"
]
},
{
"cell_type": "markdown",
"id": "bc96a67b-2ce1-4d5a-ace3-fa0747521e8a",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Explore the branch (ls, git status):"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cdeea98b-1924-47fe-9fa4-fea0d36d9c30",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/FAIR_bioinfo_github\n",
"ls -lah\n",
"git status"
]
},
{
"cell_type": "markdown",
"id": "f046d63a-cc57-4311-ba34-73661658bf68",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"The branch `branch_myfn` looks at a strict copie of the origin, the `master` branch."
]
},
{
"cell_type": "markdown",
"id": "28c2746c-a890-498f-ad2a-918aa5c9265a",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Realise a git cycle: i) change the `README.md` file by adding your firstname to the authors list, ii) add the file to the staged area, and iii) commit:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ed027089-b560-47d6-82a8-43776fc0160e",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/FAIR_bioinfo_github\n",
"echo \"- my firstname \" >> fn.txt ; \n",
"cat < fn.txt >> README.md ; rm fn.txt # add fisrtname\n",
"more README.md ; echo \"----------\" # check adding\n",
"git status # check status"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1f31baf4-0b1c-48fb-b420-0d8753a90594",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/FAIR_bioinfo_github\n",
"git add README.md # add to the staged area\n",
"git status"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f05b2a47-86b2-449f-8c68-7648a6f19b55",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"git commit -m \"add firstname\" # commit step\n",
"git status"
]
},
{
"cell_type": "markdown",
"id": "063eb2ba-d38c-4bd1-b8b6-a70bb2e07013",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Once you have check that the changes are corretcs, back to the `master` branch.\n",
"Check the version of `README.md` file:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "864f25eb-5b22-49f5-86cf-33726e9f8c8f",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/FAIR_bioinfo_github\n",
"git checkout master\n",
"more README.md"
]
},
{
"cell_type": "markdown",
"id": "b65e92d0-cd6a-46d8-ac34-82197177b024",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"It is the version before the change in the `branch_myfn` branch."
]
},
{
"cell_type": "markdown",
"id": "b639a71c-6ba1-4b64-9a3c-e811e24717c6",
"metadata": {
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"**`merge`** and **`delete`** the `branch_myfn`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "63ad2cc6-953e-4402-b397-e6e32df36b5c",
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"%%sh\n",
"cd ${PWD}/FAIR_bioinfo_github\n",
"git merge branch_myfn # merge the branch to master\n",
"echo \"------------\"\n",
"more README.md\n",
"echo \"------------\"\n",
"git branch -d branch_myfn # -d = delete\n",
"git branch"
]
},
{
"cell_type": "markdown",
"id": "61ee014f-c92c-4cbb-8629-11c92ebc57ba",
"metadata": {
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"## Conclusion\n",
"\n",
"You now know how to version a project with the Git commands in a Git cycle (change/add to stage/commit/push).\n",
"\n",
"And you also have use a Git branch to test a new code functionality/version before save it.\n",
"\n",
"\n",
"## References\n",
"\n",
"- [version control, wikipedia](https://en.wikipedia.org/wiki/Version_control)\n",
"- [git quick guide, tutorial point](https://www.tutorialspoint.com/git/git_quick_guide.htm)\n",
"- [git getting started](https://www.powershellmagazine.com/2015/07/13/git-for-it-professionals-getting-started-2/)"
]
},
{
"cell_type": "markdown",
"id": "824e11c6-dddf-487e-a487-67c82856e616",
"metadata": {},
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.4"
}
},
"nbformat": 4,
"nbformat_minor": 5
}