{ "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", "\"git\"" ] }, { "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", "\"Final?\"\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", "\"PLOS" ] }, { "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", "\"Git\"\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", "\"GitHub\"\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", "\"Revision" ] }, { "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", "[\"Git](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", " [\"Git](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 }