## Branches ### Jan Kozubowski --- ## Git architecture ### Commits 1. Commits history forms a [Directed Acyclic Graph (DAG)](https://journal.hexmos.com/git-uses-dag/) 2. Each commit contains: - a snapshot of the repository - metadata (author, timestamp, message) - a pointer to its parent commit - unique hash id --- ![image](https://hackmd.io/_uploads/SJlFDAhtWl.png) [Source](https://pavolkutaj.medium.com/explaining-dag-directec-acyclic-graphs-as-utilized-in-git-a7acccf2a6c6) --- <div style="transform: scale(0.55); transform-origin: top;"> ### Branches 1. Branch is simply a pointer to a specific commit 2. No files are copied while creating a new branch 3. After merging two branches node (commit) has two parents in DAG - check `git log` after the merge ![image](https://hackmd.io/_uploads/S1RK50cFbe.png) [Source](https://www.graphapp.ai/engineering-glossary/git/dag-directed-acyclic-graph#:~:text=Branches%20and%20Merging&text=In%20the%20context%20of%20the,node%20with%20two%20parent%20commits.) </div> --- ## HEAD Your current position in the repository (or the checkout commit that is the basis for your work) Special pointer to track the current location. 1. HEAD usually points to a branch name 2. Branch name points to the last commit When you make a commit: 1. Git moves the branch forward 2. HEAD moves with it ---- <!-- .slide: style="font-size: 0.87em;" --> ## Investigate .git folder ``` # Show all the files in the directory (including hidden folder) ls -la # Access .git folder cd .git ls # Show HEAD cat HEAD # Switch branch cd .. git switch -c new_branch # Add some sample file and commit touch text.txt git add . git commit -m "Add text txt file" # Check HEAD after the change cat .git/HEAD ``` Check refs/heads in .git folder and open a file for specific branch. What is inside? --- ## Detached HEAD When HEAD point to the specific commit not to the branch Effect: you are not on a specific branch ``` # Moves HEAD to a different state in a repository history: git checkout <commit-hash> # or git switch --detach <commit-hash> ``` --- ![image](https://hackmd.io/_uploads/rJcnXMcFbx.png) [Source](https://blog.git-init.com/what-is-head-in-git/) --- ![image](https://hackmd.io/_uploads/HkxfaDzcYbg.png) Created with [visualizing-git](https://github.com/git-school/visualizing-git) --- ## Why do we use detached HEAD? 1. Temporarily experimenting 2. Testing old code (investigate a bug in old version) 3. Creating new branch starting from old commit --- <!-- .slide: style="font-size: 0.95em;" --> ## Risk associated with detached HEAD 1. You can lose work 2. Commits in detached state can become unreachable - you won't see them after switching branch and triggering `git log` (try: `git reflog`) 4. Finally they will be removed by git (garbage collection) 5. Easy to accidentally discard work (switch branch) ``` Warning: you are leaving commits behind ``` --- ## Example workflow ![image](https://hackmd.io/_uploads/r1DQkm5KWl.png) --- ![image](https://hackmd.io/_uploads/HyQOkQ5t-g.png) --- <!-- .slide: style="font-size: 0.85em;" --> ![image](https://hackmd.io/_uploads/ryS5JX9YWe.png) You can also do these three steps using one command: ``` # Modern way: git switch -c <new-branch-name> <commit-hash> # Old way: git checkout -b <new-branch-name> <commit-hash> ``` --- ## Switching branches with uncommited changes Scenarios: 1. If there is no conflict between changes git will bring the them to the new branch (they will still be in working directory) 2. If there are conflicting changes git will show error message. You need to commit changes first and then change branch ``` error: Your local changes to the following files would be overwritten by checkout ``` --- ## Exercise: detached HEAD and braches 1. Initialize a repository: `git init branch-demo` 2. `cd branch-demo` 3. You can open VSC: `code .` Open terminal in VSC and continue 4. Create a history of three commits (you can create and change sample txt files) ``` # repeat 3 times git add . git commit -m "Message" ``` 3. Check hitory: `git log --oneline` --- 5. Checkout a specific commit (you are now in detached HEAD): ``` git switch --detach HEAD~1 # Check git status: git status # See "future" commits: git log HEAD..master # See all history: git log --all --graph --oneline ``` --- 5. Change something in txt files. Stage and commit changes. 6. Show all the branches: `git branch` 7. Create a new branch starting from a new commit: ``` git switch -c branch_based_on_old_commit ``` 8. Check all the branches and logs: ``` git branch git log --all --graph --oneline ``` Our work is preserved on a new branch. --- ## Exercise: lost commits 1. In branch-demo repo go back to master branch: ``` git switch master ``` 2. Add two commits to the history 3. Run `git log` and copy hash id of some old commit 4. Use detached HEAD (paste hash id) ``` git switch --detach <commit-hash-id> ``` --- 5. Add two sample commits and check logs (`git log`) 6. Switch branch back to master ``` git switch master ``` What warning is git showing? Run `git log` and try to find these commits. Try `git reflog`! --- ## Merging and rebasing --- ## Fast-forward merge ![image](https://hackmd.io/_uploads/Syi3AnrcZe.png) [Source](https://lukemerrett.com/different-merge-types-in-git/) --- ## Merge commit ![image](https://hackmd.io/_uploads/rJ7PA2BqWl.png) [Source](https://lukemerrett.com/different-merge-types-in-git/) --- <!-- .slide: style="font-size: 0.87em;" --> ## Exercise: merge ``` # 1. Create a git repo mkdir merge-demo && cd merge-demo git init # 2. Add commit history echo "commit 1 master" > commit_log.txt # Stage and commit. # 3. Add new line to the file echo "commit 2 master" >> commit_log.txt # Stage and commit. # Print the file: cat commit_log.txt # 4. Create a new branch feature: git switch -c feature #or git checkout -b feature ``` --- <!-- .slide: style="font-size: 0.87em;" --> ``` # 5. Make a divergent history on feature and master echo "commit 1 feature" >> commit_log.txt # Stage and commit. # 6. Switch to master and make commits git switch master echo "commit 3 master" > new_file.txt # Stage and commit. # Check the graph git log --oneline --graph --decorate --all ``` <div style="transform: scale(0.55); transform-origin: top;"> ![image](https://hackmd.io/_uploads/rJYwyCS9Wg.png) </div> --- ``` # 7. Merge git switch master cat commit_log.txt git merge feature cat commit_log.txt git log --oneline --graph --decorate --all # Show parents: git show ``` <div style="transform: scale(0.55); transform-origin: top;"> ![image](https://hackmd.io/_uploads/BJ8R1CBqbe.png) </div> --- ## Squash and merge ![image](https://hackmd.io/_uploads/H1qVy6S5bg.png) [Source](https://lukemerrett.com/different-merge-types-in-git/) --- ## Rebase and merge ![image](https://hackmd.io/_uploads/HkCw1pSqWg.png) [Source](https://lukemerrett.com/different-merge-types-in-git/) --- ## Exercise: rebase ``` # 1. Go back to our merge-demo repo cd merge-demo # 2. We want to delete a merge commit from previous exercise. Check where the HEAD was: git reflog # Copy the commit shash just before the merge commit git reset --hard <copied-hash-id> # Show file cat commit_log.txt ``` <div style="transform: scale(0.55); transform-origin: top;"> ![image](https://hackmd.io/_uploads/BJduNRHcWg.png) </div> --- ``` 3. Switch to feature branch git switch feature # Show log git log --oneline --graph --decorate --all # 4. Rebase: feature commits will be replayed on top of master git rebase master # Show log git log --oneline --graph --decorate --all ``` <div style="transform: scale(0.55); transform-origin: top;"> ![image](https://hackmd.io/_uploads/S1lVLAHqZx.png) </div> --- ``` # 5. Merge branches git switch master git merge feature # 6. Show history git log --oneline --graph --decorate --all ``` What kind of merge is that? What are we missing in the history compared to the previous exercise? <div style="transform: scale(0.45); transform-origin: top;"> ![image](https://hackmd.io/_uploads/rJaRLRr5-l.png) </div> --- Git merge mess ![image](https://hackmd.io/_uploads/rk7bLbL9Wl.png) [Source](https://www.edureka.co/blog/git-rebase-vs-merge/) --- ## When to use rebase? 1. With `git merge` history gets messy with merge commits 2. Rebase often makes fast-forward merge possible (without commit) 3. Clean, linear history 4. Easy to review Don't use rebase in shared/remote branches! Rebase rewrites history to make it cleaner. --- ## Exercise: merge conflict ``` # In merge-demo repo switch to master and create file git switch master echo "Hello world" > file.txt # Stage and commit # Switch to feature git switch feature echo "Hello world from feature branch" > file.txt # Stage and commit ``` --- ``` # Switch to master and merge git switch master git merge feature # Open the conflicted file and resolve the conflict # Check changed files git status # Stage and commit: git add file.txt git commit -m "Resolve merge conflict" # Show log: git log --oneline --graph --decorate --all ```
{"description":"Commits history forms a Directed Acyclic Graph (DAG)","title":"Branches","contributors":"[{\"id\":\"9b02b446-e66a-4b87-ad0b-21574e73f20f\",\"add\":12219,\"del\":2891,\"latestUpdatedAt\":1774289815705}]"}
    57 views