|
|
---
|
|
|
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!
|
|
|
|
|
|

|
|
|
|
|
|
### 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
|
|
|
|
|
|

|
|
|
|
|
|
* 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'>
|
|
|
|
|
|

|
|
|
|
|
|
</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)
|
|
|
|
|
|

|
|
|
|
|
|
### 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
|
|
|
|
|
|

|
|
|
|
|
|
## 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: Beginner’s 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. |