# Git Demystified ### Master Git by understanding its basics Péter Budai --- ## Agenda [History and properties](#/history) [Anatomy of a repo](#/anatomy) [Branching and merging](#/branches) [Collaboration](#/remotes) [Submodules](#/submodules) --- ## Git history Created by Linus Torvalds for the Linux kernel source Started in 2005, it took him 4 days to get it self-hosted and 2 more months to host Linux 3 years later, GitHub was launched Now, it serves 30M+ users, 100M+ repos and 3B+ lines of code --- ## Git properties * Distributed * The complete history is present locally * Designed for non-linear workflows * Easy branching & merging * Fast and scalable * Simple, generic-purpose data structure * More like a filesystem than a SCM --- ## Creating a repo New, empty repository git init demo-repo Obtain an existing repository git clone git@github.com:peterbudai/demo-repo.git The created repository is a complete, standalone entity. From now on, all operations are local. --- ## Anatomy of a repo ``` . |-- .git | |-- config | |-- ... | |-- objects | `-- refs |-- .gitignore |-- README.md `-- src `-- main.py ``` * Repository * Local configuration * Object database * Refs * Working directory --- ## Working with files  In the working directory, files can be either: * Tracked * Unchanged * Modified or deleted * Staged * Modified or deleted * Untracked * Ignored --- ## Commiting Recording the changes in the working directory as new version and stores it in the repository. One-step _(will not add untracked files!)_ git commit -a -m "Commit message" Using staging area git add [-A] git add ... git commit -m "Commit message" Commiting always takes a whole "snapshot" of the current file structure, not just records the changes. --- ## Repository Object database * content-addressable, append-only database * stores files, directories, commits, tags * each version is a different object, stored as a whole * they may contain pointers to others by their address References and tags * named pointers to commit objects * simple text files storing object addresses --- ## Object addresses SHA1 hash of the contents. For files $ echo -e "blob $(cat README.md | tr -d '\r' | wc -c)\x00$(cat README.md | tr -d '\r')" | sha1sum cffb72bf4f310e052924dca85235d03aebf479e6 *- $ openssl zlib -d -in .git/objects/cf/fb72bf4f310e052924dca85235d03aebf479e6 Even the slightest change will result in a different SHA1 hash, thus a different object. -- ## Object addresses SHA1 hash of the contents. For directories: $ git cat-file -p f56600 100644 blob 43ae0e2a6c6d8fca34872506ca0f2e64194fec7c .gitignore 100644 blob cffb72bf4f310e052924dca85235d03aebf479e6 README.md 040000 tree 7a02d51d8835b74b82d008e0273e6de31e819515 src Will change if an item is added, removed or its content or permissions change (recursively). -- ## Object addresses SHA1 hash of the contents. For commits: $ git cat-file -p 60eb0d tree f566009872bdd562f7c76e4220eeb1ae7b2406fd parent fdf4b4da0d4d388b0276f0fde71b29e6470ec36f author Péter Budai <...> 1549380900 +0100 committer Péter Budai <...> 1549383923 +0100 Added Hello World. If any content changes (even the commit message!), it will result in a different commit object. _One cannot change an already commited version!_ --- ## Working tree snapshots After the first commit ``` commit fdf4b4da0d4d388b0276f0fde71b29e6470ec36f (HEAD) Author: Péter Budai <...> Date: Tue Feb 5 15:23:58 2019 +0100 Added README. . |-- .gitignore `-- README.md ``` ```dot digraph { bgcolor=transparent; commit1 [shape=box; label="Commit\nfdf4b4"] root1 [shape=folder; label="/\n129c9f"] gitignore1 [shape=note; label=".gitignore (v1)\ne69de2"] readme [shape=note; label="README.md\ncffb72"] commit1 -> root1 root1 -> gitignore1 root1 -> readme } ``` -- ## Working tree snapshots After the second commit ``` commit 60eb0d3ad8babf1b082844472fa9c027fc0eb552 (HEAD -> master) Author: Péter Budai <...> Date: Tue Feb 5 16:35:00 2019 +0100 Added Hello World. . |-- .gitignore |-- README.md `-- src `-- main.py ``` ```dot digraph { bgcolor=transparent; commit1 [shape=box; label="Commit\nfdf4b4"] root1 [shape=folder; label="/\n129c9f"] gitignore1 [shape=note; label=".gitignore (v1)\ne69de2"] readme [shape=note; label="README.md\ncffb72"] commit1 -> root1 root1 -> gitignore1 root1 -> readme commit2 [shape=box; label="Commit\n60eb0d"] root2 [shape=folder; label="/\nf56600"] gitignore2 [shape=note; label=".gitignore (v2)\n43ae0e"] src [shape=folder; label="src\n7a02d5"] mainpy [shape=note; label="main.py\ne33bff"] commit1 -> commit2 [dir=back] commit2 -> root2 root2 -> gitignore2 root2 -> readme root2 -> src src -> mainpy {rank = same; commit1; commit2} } ``` --- ## Commit history All commits have one or more parents: they form a backward-linked list of the changes in the repository. ```bash $ git log --oneline d5e9466 d5e9466 Fixed typo. faab7c2 Factored out string constant. 74e95f3 Factored print function out 60eb0d3 Added Hello World. fdf4b4d Added README. $ git log --oneline 631a42b 631a42b Pretty-printing 74e95f3 Factored print function out 60eb0d3 Added Hello World. fdf4b4d Added README. ``` ```bash $ git config --global alias.graph \ log --oneline --decorate --graph $ git graph --all * d5e9466 Fixed typo. * faab7c2 Factored out string constant. | * 631a42b Pretty-printing |/ * 74e95f3 Factored print function out * 60eb0d3 Added Hello World. * fdf4b4d Added README. ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 [dir=back] {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` It is easy to look up parents and previous states, but not vice-versa. --- ## Branches We can create meaningful labels pointing to a commit... git branch constant ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] master [shape=plain] constant [shape=plain] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 [dir=back] master -> master4 HEAD -> master4 constant -> branch2 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` ...to make it easier to navigate between versions... $ git checkout constant Switched to branch 'constant' --- ## Working on a branch ...or to do changes that does not affect other branches. $ git checkout constant $ git commit -am "Fixed typo." ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [style=invis] master [shape=plain] constant [shape=plain; label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 -> branch2 [style=invis] master3 -> branch1 [dir=back] master -> master4 HEAD -> branch1 constant -> branch1 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"; style=bold] master [shape=plain] constant [shape=plain; label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 [dir=back] master -> master4 HEAD -> branch2 constant -> branch2 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` The current branch (and HEAD) moves along with the new commits. All other refs stay put. --- ## HEAD HEAD is a special reference that always points to the currently checked out commit. ``` $ git checkout fdf4b4 You are in 'detached HEAD' state. ... HEAD is now at fdf4b4d Added README. ``` Even if no other branch points to that commit. ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] master [shape=plain] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master -> master4 HEAD -> master3 {rank = same; master1; master2; master3; master4} } ``` -- ## Detached HEAD Be cautious with detached HEAD. ``` $ git commit -m "..." $ git checkout master $ git gc ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] master [shape=plain] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master -> master4 HEAD -> master3 {rank = same; master1; master2; master3; master4} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"; style=bold] master [shape=plain] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 [dir=back] master -> master4 HEAD -> branch1 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"; style=dashed] master [shape=plain; label="master*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 [dir=back] master -> master4 HEAD -> master4 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] master [shape=plain; label="master*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master -> master4 HEAD -> master4 {rank = same; master1; master2; master3; master4} } ``` _GC removes all objects that no other object or ref points to!_ --- ## Tags Tags are just immutable, append-only branches, to label a specific point in the history. ``` git tag v1.0 ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] master [shape=plain; label="master*"] tag1 [shape=underline; label="v1.0"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master -> master4 HEAD -> master4 tag1 -> master3 {rank = same; master1; master2; master3; master4} } ``` -- ## Annotated tags They can carry some metadata (eg. description or a digital signature) ``` git tag -a v0.8 -m "Beta release" ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] master [shape=plain; label="master*"] tag1 [shape=underline; label="v0.8"] tag2 [shape=note; label="Tag\ne073b7"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master -> master4 HEAD -> master4 tag1 -> tag2 -> master3; {rank = same; master1; master2; master3; master4} } ``` --- ## Merging in general Diverging changes on different branches need to be unified eventually. The end result is always a commit that contains changes from both sides, but we have two options of how we achieve it: _merging_ or _rebasing_ Either way, Git usually does a quite good job on finding the middle ground, and we only need to resolve _merge conflicts_ when both sides changed the exact same lines. --- ## Merge operation git merge master Will create a new _merge commit_ on the current branch, with both parents ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] master [shape=plain] constant [shape=plain; label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch1 -> branch2 [dir=back] master -> master4 HEAD -> branch2 constant -> branch2 {rank = same; master1; master2; master3; master4} {rank = same; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] branch3 [shape=box; label="Commit\n8c31cb7"; style=bold] master [shape=plain] constant [shape=plain, label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 -> branch3 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 -> branch3 [dir=back] master -> master4 HEAD -> branch3 constant -> branch3 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2; branch3} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] branch3 [shape=box; label="Commit\n8c31cb7"] branch4 [shape=box; label="Commit\nb3199dd"; style=bold] master [shape=plain] constant [shape=plain, label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 -> branch3 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 -> branch3 -> branch4 [dir=back] master -> master4 HEAD -> branch4 constant -> branch4 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2; branch3; branch4} } ``` --- ## Rebase operation git rebase master Will _recreate_ all commits _one-by-one_ as they would be authored on top of the other branch. ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] master [shape=plain] constant [shape=plain; label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 [dir=back] master -> master4 HEAD -> branch2 constant -> branch2 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"; style=dashed] branch2 [shape=box; label="Commit\nd5e9466"] rebase1 [shape=box; label="Commit\n7bc489a"; style=bold] master [shape=plain] constant [shape=plain; label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 -> rebase1 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 [dir=back] rebase1 -> branch1 [dir=none; style=dashed] master -> master4 HEAD -> rebase1 constant -> branch2 {rank = same; master1; master2; master3; master4; rebase1} {rank = same; branch0; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"; style=dashed] branch2 [shape=box; label="Commit\nd5e9466"; style=dashed] rebase1 [shape=box; label="Commit\n7bc489a"; style=bold] rebase2 [shape=box; label="Commit\n32c2d79"; style=bold] master [shape=plain] constant [shape=plain; label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 -> rebase1 -> rebase2 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 [dir=back] rebase1 -> branch1 [dir=none; style=dashed] rebase2 -> branch2 [dir=none; style=dashed] master -> master4 HEAD -> rebase2 constant -> branch2 {rank = same; master1; master2; master3; master4; rebase1; rebase2} {rank = same; branch0; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"; style=dashed] branch2 [shape=box; label="Commit\nd5e9466"; style=dashed] rebase1 [shape=box; label="Commit\n7bc489a"; style=bold] rebase2 [shape=box; label="Commit\n32c2d79"; style=bold] master [shape=plain] constant [shape=plain; label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 -> rebase1 -> rebase2 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 [dir=back] master -> master4 HEAD -> rebase2 constant -> rebase2 {rank = same; master1; master2; master3; master4; rebase1; rebase2} {rank = same; branch0; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] rebase1 [shape=box; label="Commit\n7bc489a"; style=bold] rebase2 [shape=box; label="Commit\n32c2d79"; style=bold] master [shape=plain] constant [shape=plain; label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 -> rebase1 -> rebase2 [dir=back] master -> master4 HEAD -> rebase2 constant -> rebase2 {rank = same; master1; master2; master3; master4; rebase1; rebase2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] rebase1 [shape=box; label="Commit\n7bc489a"] rebase2 [shape=box; label="Commit\n32c2d79"] rebase3 [shape=box; label="Commit\n1594b24"; style=bold] master [shape=plain] constant [shape=plain; label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 -> rebase1 -> rebase2 -> rebase3 [dir=back] master -> master4 HEAD -> rebase3 constant -> rebase3 {rank = same; master1; master2; master3; master4; rebase1; rebase2; rebase3} } ``` --- ## Merge vs rebase Rebase results in a cleaner history, for the price of rewriting it! Avoid rebase on already pushed commits, as it may result in diverging histories if the deleted commits were already fetched by others. ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] rebase1 [shape=box; label="Commit\n7bc489a"] rebase2 [shape=box; label="Commit\n32c2d79"] rebase3 [shape=box; label="Commit\n1594b24"; style=bold] master [shape=plain] constant [shape=plain; label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 -> rebase1 -> rebase2 -> rebase3 [dir=back] master -> master4 HEAD -> rebase3 constant -> rebase3 {rank = same; master1; master2; master3; master4; rebase1; rebase2; rebase3} } ``` Good for `pull`, but avoid pulling after a local merge. -- ## Merge vs rebase Merge always adds extra commit (except fast-forward), but it is non-destructive. The resulting graph shows the change history more realistically, but harder to follow. ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] branch3 [shape=box; label="Commit\n8c31cb7"] branch4 [shape=box; label="Commit\nb3199dd"; style=bold] master [shape=plain] constant [shape=plain, label="constant*"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 -> branch3 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 -> branch3 -> branch4 [dir=back] master -> master4 HEAD -> branch4 constant -> branch4 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2; branch3; branch4} } ``` Good for merging in large changesets of many commits. --- ## Remotes A _remote_ is a link to another copy of the repository * on another disk volume, * on a network share or a remote computer, * on a Git hosting service (like GitHub). Set a new remote named `backup` ``` git remote add backup file:///mnt/nas/demo-repo.git ``` Cloning automatically sets the source as `origin` ``` git clone git@github.com:peterbudai/demo-repo.git ``` --- ## Synchronizing between repos Synchronizing is simply done by exchanging the missing objects and updating references. * The connections between objects will automatically be restored after all objects arrived Receiving changes from a remote git fetch origin Sending changes to a remote (branch) git push backup master:master --- ## fetch Fetch does not change anything in the working copy, just updates the database. ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [style=invis] master [shape=plain] rmaster [shape=plain; label="origin/master"] constant [shape=plain; label="constant*"] rconstant [shape=plain; label="origin/constant"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 -> branch2 [style=invis] master3 -> branch1 [dir=back] master -> master4 rmaster -> master4 HEAD -> branch1 constant -> branch1 rconstant -> branch1 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] master [shape=plain] rmaster [shape=plain; label="origin/master"] constant [shape=plain; label="constant*"] rconstant [shape=plain; label="origin/constant"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 [dir=back] master -> master4 rmaster -> master4 HEAD -> branch1 constant -> branch1 rconstant -> branch2 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` Remote references are kept separately in the local database. --- ## Tracking branches You can "connect" a local branch to a remote branch. Git will try to update the other end during pull and push. git branch -u origin/constant or git push -u origin master --- ## Pull git fetch && git rebase origin/master In a simple case it just updates the references ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [style=invis] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 [dir=back] master -> master3 rmaster -> master3 HEAD -> master3 {rank = same; master1; master2; master3; master4} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master -> master3 rmaster -> master4 HEAD -> master3 {rank = same; master1; master2; master3; master4} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master -> master4 rmaster -> master4 HEAD -> master4 {rank = same; master1; master2; master3; master4} } ``` --- ## Pull git fetch && git rebase origin/master If there were local changes, the local and remote branch need to be merged ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [style=invis] branch2 [style=invis] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 -> branch2 [style=invis] master -> master4 rmaster -> master3 HEAD -> master4 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 [dir=back] master -> master4 rmaster -> branch2 HEAD -> master4 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"; style=dashed] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 [dir=back] master -> master4 rmaster -> branch2 HEAD -> master4 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n9eae16b"; style=bold] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 -> branch1 -> branch2 -> master4 [dir=back] master3 -> branch0 -> branch1 [style=invis] master -> master4 rmaster -> branch2 HEAD -> master4 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n9eae16b"] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 -> branch1 -> branch2 -> master4 [dir=back] master -> master4 rmaster -> branch2 HEAD -> master4 {rank = same; master1; master2; master3; master4; branch1; branch2} } ``` --- ## Push Unlike to fetch, it sends only the objects relevant to the tracking branch. git push In a simple case it just updates the references. ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master -> master4 rmaster -> master3 HEAD -> master4 {rank = same; master1; master2; master3; master4} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master -> master4 rmaster -> master4 HEAD -> master4 {rank = same; master1; master2; master3; master4} } ``` --- ## Push conflict If there were changes at both side, the push will be rejected $ git push ! [rejected] master -> master (non-fast forward) error: failed to push some refs to 'git@github.com:peterbudai/demo-repo.git You need to resolve the situation locally first and push again $ git pull $ git push --- ## Force push ``` git push -f ``` It should be avoided if possible, as it may result in diverging histories if the deleted commits were already fetched by others. ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 [dir=back] master -> branch2 rmaster -> master4 HEAD -> branch2 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] master4 [shape=box; label="Commit\n631a42b"; style=dashed] branch0 [style=invis] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 -> master4 [dir=back] master3 -> branch0 -> branch1 [style=invis] master3 -> branch1 -> branch2 [dir=back] master -> branch2 rmaster -> branch2 HEAD -> branch2 {rank = same; master1; master2; master3; master4} {rank = same; branch0; branch1; branch2} } ``` ```dot digraph { bgcolor=transparent; master1 [shape=box; label="Commit\nfdf4b4d"] master2 [shape=box; label="Commit\n60eb0d3"] master3 [shape=box; label="Commit\n74e95f3"] branch1 [shape=box; label="Commit\nfaab7c2"] branch2 [shape=box; label="Commit\nd5e9466"] master [shape=plain; label="master*"] rmaster [shape=plain; label="origin/master"] HEAD [shape=plain] master1 -> master2 -> master3 -> branch1 -> branch2 [dir=back] master -> branch2 rmaster -> branch2 HEAD -> branch2 {rank = same; master1; master2; master3; branch1; branch2} } ``` --- ## Submodules For "mounting" the contents of another repository under a subfolder. ```dot digraph { subgraph cluster2 { label="Submodule"; labeljust=l sub1 [shape=box; label="Commit\ncf83e13"] sub2 [shape=box; label="Commit\n8fc1c14"] sub3 [shape=box; label="Commit\n50b5715"] sub1 -> sub2 -> sub3 [dir=back] {rank=same; sub1; sub2; sub3} } subgraph cluster1 { label="Main repo"; labeljust=l commit1 [shape=box; label="Commit\nfdf4b4"] root1 [shape=folder; label="/\n129c9f"] readme1 [shape=note; label="README.md (v1)\ncffb72"] lib1 [shape=cds; label="lib\n8fc1c14"] commit1 -> root1 root1 -> readme1 root1 -> lib1 lib1 -> sub2 } bgcolor=transparent; } ``` --- ## Updating a submodule It always points to a given commit of the other repo. ```dot digraph { bgcolor=transparent; subgraph cluster2 { label="Submodule"; labeljust=l sub1 [shape=box; label="Commit\ncf83e13"] sub2 [shape=box; label="Commit\n8fc1c14"] sub3 [shape=box; label="Commit\n50b5715"] sub1 -> sub2 -> sub3 [dir=back] {rank=same; sub1; sub2; sub3} } subgraph cluster1 { label="Main repo"; labeljust=l commit1 [shape=box; label="Commit\nfdf4b4"] root1 [shape=folder; label="/\n129c9f"] readme1 [shape=note; label="README.md (v1)\ncffb72"] lib1 [shape=cds; label="lib\n8fc1c14"] commit1 -> root1 root1 -> readme1 root1 -> lib1 lib1 -> sub2 } } ``` ```dot digraph { bgcolor=transparent; subgraph cluster2 { label="Submodule"; labeljust=l sub1 [shape=box; label="Commit\ncf83e13"] sub2 [shape=box; label="Commit\n8fc1c14"] sub3 [shape=box; label="Commit\n50b5715"] sub1 -> sub2 -> sub3 [dir=back] {rank=same; sub1; sub2; sub3} } subgraph cluster1 { label="Main repo"; labeljust=l commit1 [shape=box; label="Commit\nfdf4b4"] root1 [shape=folder; label="/\n129c9f"] readme1 [shape=note; label="README.md (v1)\ncffb72"] lib1 [shape=cds; label="lib\n8fc1c14"] commit1 -> root1 root1 -> readme1 root1 -> lib1 lib1 -> sub2 commit2 [shape=box; label="Commit\nfdf4b4"] root2 [shape=folder; label="/\n8c66b2"] readme2 [shape=note; label="README.md (v2)\n39aa14"] commit1 -> commit2 [dir=back] commit2 -> root2 root2 -> readme2 root2 -> lib1 {rank=same; commit1; commit2} } } ``` ```dot digraph { bgcolor=transparent; subgraph cluster2 { label="Submodule"; labeljust=l sub1 [shape=box; label="Commit\ncf83e13"] sub2 [shape=box; label="Commit\n8fc1c14"] sub3 [shape=box; label="Commit\n50b5715"] sub1 -> sub2 -> sub3 [dir=back] {rank=same; sub1; sub2; sub3} } subgraph cluster1 { label="Main repo"; labeljust=l commit1 [shape=box; label="Commit\nfdf4b4"] root1 [shape=folder; label="/\n129c9f"] readme1 [shape=note; label="README.md (v1)\ncffb72"] lib1 [shape=cds; label="lib\n8fc1c14"] commit1 -> root1 root1 -> readme1 root1 -> lib1 lib1 -> sub2 commit2 [shape=box; label="Commit\nfdf4b4"] root2 [shape=folder; label="/\n8c66b2"] readme2 [shape=note; label="README.md (v2)\n39aa14"] commit2 -> root2 root2 -> readme2 root2 -> lib1 commit3 [shape=box; label="Commit\n4050b5"] root3 [shape=folder; label="/\n495991"] lib2 [shape=cds; label="lib\n50b5715"] commit3 -> root3 root3 -> readme2 root3 -> lib2 lib2 -> sub3 commit1 -> commit2 -> commit3 [dir=back] {rank=same; commit1; commit2; commit3} } } ``` --- ## Further reading [Pro Git](https://git-scm.com/book/en/v2) book by Scott Chacon and Ben Straub (free) [Source code](https://github.com/peterbudai/git-demystified) of this presentation --- # Thank you