<style> .reveal { font-size: 26px; } </style> ## Git: Branches, merging, rebasing Wojciech Hardy link: https://hackmd.io/@WHardy/RR-git3 --- ## Branches Part of the typical workflow ![](https://i.imgur.com/gUWMU0q.png) Source: [Harness](https://www.harness.io/blog/git-branching) --- ## Branches -- advantages Main branch keeping a clean history of releases Room for experimentation without breaking anything Simultaneous work for many people Easy to reverse work on a feature and continue with main --- ## Branches can have branches ![](https://i.imgur.com/9G6xsil.png =600x) (Source: [Git Better](https://gitbetter.substack.com/p/how-to-work-in-multiple-git-branches)) --- ## Branches - how do they work? Recall `master` or `main` branch is an indicator You create a new branch using `git branch <branch-name>` The new branch is a new indicator with its own `staging area` and `working directory` Scroll down to see what happens step-by-step ---- ## We start with one branch: `main` ![](https://i.imgur.com/mtTy9kH.png) Recall: `HEAD` points to 'where we currently are' `main` points to the latest commit on branch ---- ## We create a new branch that we name "feature" with `git branch feature` ![](https://i.imgur.com/gnxYmVD.png) This creates a new indicator pointing to where we are. But `HEAD` still points to the `main` branch. ---- ## We use `git checkout feature` to get to the new "feature" branch ![](https://i.imgur.com/u3DVeC4.png) The `HEAD` indicator goes to the `feature` branch. --- ## Ok, let's do it, setup: 1) In folder RR_git3 create bare central repository and two cloned ones. `mkdir RR_git3` `cd RR_git3` `git init CentralRepo --bare` `git clone CentralRepo Dev1` `git clone CentralRepo Dev2` ---- 2) As `dev1` create a short history of commits (minimum two) and push it `cd dev1` `echo "a first file" > file1.txt` `git add .` `git commit -m "Added a first file"` `echo "a second file" > file2.txt` `git add .` `git commit -m "Added a second file"` `git push` ---- 3) As `dev2` pull the information `cd ../dev2` `git pull` --- ## Let's practice branching 1) As Dev1, create a new branch named `feature1` `cd ../dev1` `git branch feature1` ---- 2) List branches (try to find the appropriate `git branch` option to do that) `git branch -l` ---- 3) Switch to the new branch (use `git checkout <branch-name>`) `git checkout feature1` --- ## What's the new workflow? You can add commits only to one of the branches at a time. When you switch between branches, Git 'refreshes' your staging area and working directories to match `HEAD`. --- ## Let's start with a simple commit history: ![](https://i.imgur.com/UJfRiNf.png) ---- ## New commits affect only the current branch ![](https://i.imgur.com/F0hSnDf.png) ---- ## New commits affect only the current branch ![](https://i.imgur.com/V4BkVu4.png) In a big project, e.g. if you're the one working on the `feature`, changes to `main` might not be yours (other people push their features/fixes, etc. in the meantime). ---- ## Let's switch to the `feature` branch with `git checkout feature` ![](https://i.imgur.com/Ii677W9.png) ---- ## And add some commits while in there ![](https://i.imgur.com/yph7crB.png) ---- ## One more ![](https://i.imgur.com/CH0mZFn.png) ---- ## At any point you can return to the `main` with `git checkout main` ![](https://i.imgur.com/xp7F7GK.png) --- ## Let's try working with branches 1) As Dev1, while in the `feature1` branch, add a file, stage and commit it. Then do a `push`. `echo "some text" > file_on_feature1.txt` `git add .` `git commit -m "First file on feature1 branch"` `git push` Read the message and follow the hints. `git push --set-upstream origin feature1` ---- 2) Switch back to the `main` branch and notice what's going on in your working directory. `ls` `git checkout main` `ls` (or look at it in Explorer/Finder) ---- 3) Add a file (do not commit it) and try changing branches. `echo "some other text" > some_file.txt` `git checkout feature1` What happens? (put it somewhere as part of a new commit and push it) `git add .` `git commit -m "New file added to history"` ---- 4) As Dev2 execute `git pull` and check `git branch -l` `cd ../Dev2` `git pull` `git branch -l` ---- 5) Execute `git checkout feature1` and check `git branch -l` again `git checkout feature1` `git branch -l` --- ## Let Dev2 do some work 1) Create a new commit on the `main` branch `git checkout main` `echo "another text" > file_dev2main.txt` `git add .` `git commit -m "A file added to main by dev2"` ---- 2) Create a new commit on the `feature1` branch `git checkout feature1` `echo "another text" > file_dev2feature.txt` `git add .` `git commit -m "A file added to feature1 by dev2"` ---- 3) Run `git status` on the `main` branch, push the changes `git checkout main` `git status` `git push` `git status` ---- 4) Run `git status` on the `feature1` branch, push the changes `git checkout feature1` `git status` `git push` `git status` --- ## There are two main ways of putting the results back together You might recall `merge` from how we united `main` with `origin/main`. ![](https://i.imgur.com/3yJ2iE8.png) ---- ## While in `main` do `git merge feature` ![](https://i.imgur.com/QoluIh0.png) Recall: `merge` creates a new commit. ---- ## You can still work on your `feature` branch It doesn't include the merge (unless you also do `git merge main` while on the branch `feature`). ![](https://i.imgur.com/RwCSUiK.png) ---- ## Now that we know the basics, let's look again Can you trace what's been going on in this project? ![](https://i.imgur.com/9G6xsil.png =600x) (Source: [Git Better](https://gitbetter.substack.com/p/how-to-work-in-multiple-git-branches)) --- ## Let's go back to the terminal 1) As Dev1 get all the newest updates for both branches. `cd ../dev1` `git pull` `git checkout main` `git pull` ---- 2) Merge the `feature1` branch to the `main` branch using `git merge <branch-name>`. Push. `git checkout main` `git merge feature1` Conflict resolution is the same as in the last week's case. `git push` --- ## The second approach is rebasing Rebasing is making your branch start from a different point ![](https://i.imgur.com/NLfjohh.png) ---- ## Let's first move to the branch we want to move (`git checkout feature`) ![](https://i.imgur.com/CH0mZFn.png) ---- ## Then `git rebase main` ![](https://i.imgur.com/rZVs88x.png) Important: `rebase` destroys original commits and creates new ones! Probably not a good thing if the original ones were shared with others or in a remote repository! --- ## Also, you don't need to rebase only within the same branch Assume you start with this: ![](https://i.imgur.com/nAkSXAw.png) ---- ## Possible troublesome situations: - `feature1` branch proved a dead end, but `feature2` is promising - `feature2` is actually unrelated to `feature1` - `main` branch introduced something relevant to `feature2` ---- ## The solution is to rebase `feature2` to `main` `git checkout feature2` `git rebase --onto main feature1 feature2` `git rebase --onto <new-base> <old-base> <moved-branch> ![](https://i.imgur.com/9DqWc03.png) ---- ## Let's go back to the large graph Can you see how `rebase` could come in handy for the lower `feature` branch? ![](https://i.imgur.com/9G6xsil.png =600x) (Source: [Git Better](https://gitbetter.substack.com/p/how-to-work-in-multiple-git-branches)) --- ## Helpful links [For more on rebasing](https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase). Read [Merging vs rebasing on Atlassian](https://www.atlassian.com/git/tutorials/merging-vs-rebasing) for a nice description of both approaches (also their individual pages). Also, take a look at [`git bisect`](https://git-scm.com/docs/git-bisect), to understand when rebasing might be helpful. ---
{"metaMigratedAt":"2023-06-17T23:18:23.078Z","metaMigratedFrom":"YAML","title":"Reproducible Research 4 - Git 3","breaks":true,"slideOptions":"{\"transition\":\"slide\"}","contributors":"[{\"id\":\"1c10bb23-6c4c-4c1b-8586-5f8d56305139\",\"add\":23141,\"del\":15099}]","description":"Wojciech Hardy"}
    487 views