You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
12 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
title: Project management 2. Git continued
marp: true
paginate: true
theme: buutti
---
<!-- headingDivider: 3 -->
<!-- class: invert -->
# Project management 2. Git continued
## Branches
* A Git repository can have multiple ***branches***
* By default, a Git repository has only one branch
* named usually `main` or `master`
* Using multiple branches makes it possible to work on new features step by step in their own ***feature branches***
* Meanwhile, the `main` branch is kept clean and in a working state
* Only when the feature is completed, the branch is merged into the `main` branch!
![bg right:30% width: 60%](imgs/git-branches.png)
### Active branch
* Even though your local repository can have multiple branches, only one of them is *active* at a given time
* `git status` tells you firsthand which branch you are on:
```
On branch main
```
## `git branch` & `git checkout`
* You can create a new branch with `git branch <branchname>`
* The new branch is not empty: it contains a copy of the code of the branch you executed this command in
* ***Note:*** `git branch <branchname>` does not make the branch active!
* To make the branch active, a.k.a "move" to the branch:
* `git checkout <branchname>`.
* For example, to move back to the main branch, use `git checkout main`
### Local vs. remote branches
* `git branch <branchname>` only creates a ***local*** branch
* When you try to push changes from a new local branch for the first time, Git nags you that a matching remote branch doesn't yet exist
* Git tells you how to create the remote branch:
* `git push --set-upstream origin <branchname>`
* Afterwards, `git push` pushes the changes to the matching remote branch
### Extra branch commands
<!-- _class: "extra invert" -->
* Handy command: `git checkout -b <branchname>`
* It's a shorthand for `git branch <branchname>` + `git checkout <branchname>`
* Get a list of local branches with `git branch`
* ...and all branches (incl. the remote ones) with `git branch -a`
* Delete a local branch with `git branch -d <branchname>`
* Remote branch can be deleted in the GitLab/etc website
* ...or with `git push origin -d <branchname>`
* ***Note:*** If deleted remote branches still show up in `git branch -a`, you can use the command `git remote prune origin` to remove them from the list.
## `git merge`
* When a feature is done (and all the broken things fixed), you want to apply, i.e., ***merge*** your changes from the feature branch to the `main` branch
1) First, checkout the `main` branch with `git checkout main`
2) Then, do a `git pull` so you have the newest version of the `main` branch
* (Someone else might have done changes to it while you were working on your feature!)
3) Then, merge your local feature branch to your local main branch with
`git merge <featurebranch>`
* It applies changes from `<featurebranch>` to the ***current active branch***
* This is where ***conflicts*** might happen (more about them [later](#conflicts))
4) Then, push your changes to remote with `git push`
### Merging with a pull/merge request
* The aforementioned procedure isn't what we usually do, though!
* `main` branch should be ***protected*** so we can't merge our new code there directly
* Instead, we do the *inverse*.
1) First, checkout the `main` branch with `git checkout main`
2) Then, do a `git pull` so you have the newest version of the `main` branch
3) Then, ***checkout back to the feature branch***.
4) Merge your local `main` branch to your local `featurebranch` with
`git merge main`
5) Then, create a ***merge request*** on GitLab (it's ***pull request*** on GitHub—they're the same thing, just named from different points of view.)
### Simplifying merging further
* ***Pro tip:*** If you're working on your feature branch, you can merge changes from the `main` branch without changing branches altogether.
1) Update your local main branch with ```git fetch origin main:main```
2) Merge your local `main` branch to your local `featurebranch` with
`git merge main`
3) Then, create a merge request/pull request
* Much easier this way!
* But what is the merge request, anyway?
### Merge request/pull request
* Merge/pull request is a formal process for merging your *remote* feature branch straight to the remote `main` branch
* This adds a layer of protection to the `main` branch: no direct merging to the `main` branch!
* Additionally, a ***code review*** can happen at this stage:
* If a code maintainer thinks your merge request needs some modifications, you can make changes to the feature branch accordingly
* A merge request is branch-specific, so when you push your changes to your feature branch, the merge request is automatically updated as well.
* If this procedure is followed strictly, no bad code should get into the main branch!
### GitHub pull request UI
![w:900px](imgs/github-pullreq.png)
* UI of a new pull request (*Pull requests > New pull request*) can be confusing...
* *base* is the branch you're merging, *compare* is the branch you're merging into.
## Exercise 1. Pushing onwards
<!--_class: "exercise invert" -->
Continue the exercise from [Git Basics](1-git-basics) or create a new repository for these exercises.
1) Create a new branch (with a name `new-feature`, for instance) in your local repository.
2) Checkout the branch, make some changes to `GitTest.md` there, and push the changes to GitLab.
3) Then, merge the changes from your `new-feature` branch to the `main` branch by using
a) `git merge` from command line
b) a merge request in GitLab
## GitLens
* To make the Git workflow easier, install the GitLens extension to VS code
* It helps in managing conflicts, comparing branches or commits
* Install it from the Extensions panel (access with ***CTRL+SHIFT+X***)
* Adds many new views to the source control tab
<div class='columns12' markdown='1'>
<div markdown='1'>
* For example:
* Commits
* Repositories
* File History
* Line History
* Branches
* Remotes
</div>
<div markdown='1'>
![](imgs/gitlens.png)
</div>
</div>
## Conflicts
* Sometimes two people have made changes in the same lines of code!
* This leads to a ***conflict***.
* Let's assume we're trying to merge changes from `featurebranch` to `main`.
* If a conflict happens, the merge does not conclude automatically.
* Instead, we need to ***fix all the conflicts*** by hand and then ***conclude the merge*** with some commands.
* Conflicting lines of code are framed by some `<<<< garbage ==== symbols >>>>`
we don't yet understand
* Before we can conclude the merge, we need to get rid of the garbage.
### Garbage explained
```c#
<<<<<<< HEAD:Player.cs
if (Input.anyKey) {
return true;
}
=======
if (Input.anyKey)
return true;
>>>>>>> featureBranch:Player.cs
```
* ***Current change*** is between `<<<<<<<` and `=======`
* old code that was already in `main`
* ***Incoming change*** is between `>>>>>>>` and `=======`
* new code coming from `featurebranch`
* Use your text editor to choose which (or some combination of both) you want to preserve
### VS Code tools
* VS Code gives us tools to make conflict resolution a relatively quick process
* Choose your preference:
* ***Accept Current Change***
(old code is preserved,
new code is removed)
* ***Accept Incoming Change*** (new code is preserved,
old code is removed)
* ***Accept Both Changes***
(both are preserved)
![bg right width:95%](imgs/vscode-conflicts.png)
### After resolving the conflict
* After resolving all conflicting files, use `git add <filename>` to add them to the commit
* Then use `git commit` to apply changes (without a message! no `-m`)
* Close the automatically opened `COMMITMSG` file.
* This finishes the merge.
* Then just `git push` to apply changes in the remote repository
* ***Note:*** If VS code is not configured properly as the Git's text editor, and you encounter an error, run `git config core.editor code --wait`
### Example Git workflow with branches
![w:1200px](imgs/merge-workflow.png)
## Exercise 2. Fixing conflicts
<!--_class: "exercise invert" -->
Continue in the Exercise 1 repository.
1) Create a new branch in your local repository, but do not checkout it just yet.
2) On the `main` branch, make some changes to `GitTest.md`, and **add & commit**.
3) Then, checkout the new feature branch.
4) Make some ***other*** changes to GitTest.md to the same line as before, **add & commit**.
5) Then, merge the changes from the `main` branch to your feature branch by using `git merge`
6) Fix the ensuing conflicts, add & commit & push.
## Undoing
* Git doesn't have a general "undo" command
* If you make a mistake, it is very case-specific what you need to do to fix it
* See [undo options here](https://docs.gitlab.com/ee/topics/git/numerous_undo_possibilities_in_git/)
* Also, [ohshitgit.com](https://ohshitgit.com/)
## `git log` & `git checkout <hashcode>`
* Use `git log` to see the commit history
* Or `git log --oneline` for a more concise version
* Press ***Q*** to quit the log view.
* The newest changes are seen on top
* On the left side of the commit message you see the ***hashcode*** of the commit
* Use `git checkout <hashcode>` to "time travel" into the commit
* ***Note:*** If you have GitLens, check the Commits view in the Source control tab to see the commit history.
*
### Reverting one file to a previous state
* Sometimes you want to revert just one file to its previous state
* You can use `git checkout <commit-hash> -- <filename>` to time travel just that file to how it was in that commit
* For this, you of course need to figure out the commit hash you want to return to
* Find that out in one of the following methods:
* `git log --oneline`
* VS Code: *Source control > File history*
* Check commits from GitHub/GitLab
## Exercise 3. Branching team effort
<!--_class: "exercise invert" -->
Work as a group for this assignment. Continue Exercise 2 from [Git basics](1-git-basics).
1) Every group member: create your own individual branch from the `main` branch
2) Then, make some changes to the `GitTest.md` file.
* Do not tell other group members what you're going to change! :D
3) Add new files as well, at least one file per group member.
4) Then, merge the changes back to the `main` branch. Fix ensuing conflicts, if any appear.
## Reading
* [Pro Git book](https://git-scm.com/book/en/v2)
* [Oh shit Git](https://ohshitgit.com/)
* [Undo possibilities](https://docs.gitlab.com/ee/topics/git/numerous_undo_possibilities_in_git/)
## Very extra: `git rebase`
<!-- _class: "extra invert" -->
* `git merge` creates a new commit for the merge process
* Sometimes that's undesirable, so an alternative is to use `git rebase`
* Unlike merge, rebase applies changes from the rebased branch
***one commit at a time***
* Whenever there's a conflict:
1) After fixing the conflict, add the conflicting file with `git add <filename>`
2) Then continue the rebase process with `git rebase --continue`
3) If you want to disregard a conflicting commit, use `git rebase --skip`
4) If you get cold feet, you can cancel the rebase with `git rebase --abort`
## Very extra: Interactive `git rebase -i`
<!-- _class: "extra invert" -->
* `git rebase` also has a hidden ***interactive*** mode used with `git rebase -i`
* This is a true swiss army knife of a Git tool for rewriting Git history
* It can:
* Squash commits into one
* Cherry-pick only certain commits
* Reword commit messages
* And more!
* See [Hackernoon: Beginners Guide to Interactive Rebasing](https://hackernoon.com/beginners-guide-to-interactive-rebasing-346a3f9c3a6d)
## Very very extra: Git submodules
<!-- _class: "extra invert" -->
* To add external code to your project from someone else's repository, Git has a neat system called *submodules*
* To add a submodule to your project, use `git submodule add <submodule-url> <folder>`
* To remove a submodule, use `git rm <path-to-submodule>`
* If you clone a project with submodules, you need to run `git submodule update --init --recursive` once.
* ***Note:*** If you don't want submodules to appear in the Source control tab of VS Code, go to settings and disable the *Git: Detect Submodules* setting.