Created
May 18, 2022 16:14
-
-
Save phamducminh/71710d08f34d2cffb9e0d601ba843b0a to your computer and use it in GitHub Desktop.
Revisions
-
phamducminh created this gist
May 18, 2022 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,228 @@ # Git Best Practices > This is the aggregation of best practices while using Git. I will continue adding them as I find other useful ones. By Minh Pham, 2020 ## Contents * [Don't panic](#dont-panic) * [Make clean, single-purpose commits](#make-clean-single-purpose-commits) * [Write meaningful commit messages](#write-meaningful-commit-messages) * [Commit early, commit often](#commit-early-commit-often) * [Don’t alter published history](#Dont-alter-published-history) * [Don’t commit generated files](#dont-commit-generated-files) * [Don’t merge upstream into your tracking branch](#dont-merge-upstream-into-your-tracking-branch) * [force-with-lease](#force-with-lease) * [Working with submodule](#working-with-submodule) * [Git reset use cases](#git-reset-use-cases) ## Don't panic | Changes | How they can get lost | | - | - | | Changes committed to git | Not at all, unless you insist | | Uncommitted changes to git-controlled files | `git checkout <file-or-directory>`<br/>`git reset --hard`<br/>Non-git commands | | Files unknown to Git | `git clean`<br/>Non-git commands | Git will try hard to preserve your changes: * Any changes you committed will be part of the reflog for at least two weeks, even if you change or abandon them. * Uncommitted changes to git-controlled-files will only get overwritten if you run one of the commands * `git checkout <file-or-directory>` * `git reset --hard` * And of course any non-git commands that change files * Files unknown to Git will only get lost with5 * `git clean` * Again, any non-git commands that change files To see almost every change that was ever8 known to git: ```bash $ git reflog --all ``` To restore all those deleted files in a folder enter the following command ```bash git ls-files -d | xargs git checkout -- ``` ## Make clean, single-purpose commits It is better to keep commits as small and focused as possible for many reasons, including: * Making code reviews more efficient. * Easier to roll back * Track these changes with your ticketing system ## Write meaningful commit messages Descriptive commit messages that concisely describe what changes are being made as part of a commit make life easier for others as well as your future self A very good heuristic for writing good commit messages is this: ``` feat: add beta sequence ^--^ ^---------------^ | | | +-> Summary in present tense. | +-------> Type: chore, docs, feat, fix, refactor, style, or test. ``` For example ``` chore: add Oyster build script docs: explain hat wobble feat: add beta sequence fix: remove broken confirmation message refactor: share logic between 4d3d3d3 and flarhgunnstow style: convert tabs to spaces test: ensure Tayne retains clothing ``` DON'T DO THIS: they are all bad commit messages ``` - fix bug - fix issue - merge code - fix build ``` ## Commit early, commit often Instead of waiting to make the commit perfect, it is better to work in small chunks and keep committing your work. ## Don’t alter published history Once a commit has been merged to an upstream default branch (and is visible to others), it is strongly advised not to alter history. While `git-rebase` is a useful feature, it should only be used on branches that only you are working with. ## Don’t commit generated files It is useful to add a `.gitignore` file in your repository’s root to automatically tell Git which files or paths you don’t want to track. ## Don’t merge upstream into your tracking branch Suppose you just started developing code on master. Your branches look like this (A and B are commits, the ‘o’ is just a visual connector): ``` --A---B----- origin/master (remote branch) \ o--- master (local tracking branch) ``` Now you commit some changes X, Y to your local tracking branch: ``` --A---B---------- origin/master \ X---Y---- master ``` and want to push them to the server. If the server is still at commit B, this will result in ``` --A---B---X---Y----- origin/master \ o--- master ``` and you are done. However, if somebody has committed changes to the server before you push, you will get an error message: ``` To [...] ! [rejected] master -> master (fetch first) error: failed to push some refs to [...] hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., ’git pull ...’) before pushing again. hint: See the ’Note about fast-forwards’ in ’git push --help’ for details. ``` Before you can fix the problem, you need to ‘git fetch’ to update the remote branch: ``` --A---B---C---D---E--- origin/master \ X---Y--------- master ``` To bring the two lines of development together, usin **rebase**: ```bash git rebase origin/master ``` if necessary deal with conflicts (that will temporarily throw your repository into a headless state) and end up with ``` --A---B---C---D---E----------- origin/master \ X’---Y’--- master ``` You have changed your commits by turning them into descendants of E (and possibly by including solutions for conflicts) and you can now push to get ``` --A---B---C---D---E---X’---Y’---- origin/master \ o--- master ``` While it is completely feasible to first fetch, then rebase, you can have both in one command: ```bash git pull --rebase ``` This is equivalent to `git fetch; git rebase origin/master`, so it is exactly what we need. ## force-with-lease When someone updates his branch and pushes it up to the remote repository, the ref pointing head of the branch will be updated. Now, unless you do a pull from the remote, your local reference to the remote will be out of date. When you go to push using --force-with-lease, git will check the local ref against the new remote and refuse to force the push. --force-with-lease effectively only allows you to force-push if no-one else has pushed changes up to the remote in the interim. ```bash git push --force origin master # DON'T DO THIS git push --force-with-lease origin master # DO THIS ``` ## Working with submodule ## Git reset use cases ### `git reset --soft`: **MOVE HEAD** It essentially undid the last `git commit` command. When you run `git commit`, Git creates a new commit and moves the branch that HEAD points to up to it. When you reset back to HEAD~ (the parent of HEAD), you are moving the branch back to where it was, without changing the **Index** or **Working Directory**. You could now update the Index and run `git commit` again to accomplish what `git commit --amend` would have done. **Use Case:** * Combine/squash a series of local commits * You are satisfied with what you end up with (in term of working tree and index) * You are not satisfied with all the commits that took you to get there ### `git reset --mixed`: **UPDATING THE INDEX** This is also the default, so if you specify no option at all (just git reset HEAD~ in this case), this is where the command will stop. It still undid your last `commit`, but also _unstaged_ everything. You rolled back to before you ran all your `git add` and `git commit` commands. **Use Case:** * Remove a couple of files in a previous commit * Split as many commits with any changes as you want ### `git reset --hard`: **UPDATING THE WORKING DIRECTORY** Undid your last commit, the `git add` and `git commit` commands, **and** all the work you did in your working directory. It’s important to note that this flag (--hard) is the only way to make the reset command dangerous, and one of the very few cases where Git will actually destroy data. Any other invocation of `reset` can be pretty easily undone, but the --hard option cannot, since it forcibly overwrites files in the Working Directory.