## git ### General Info - The `git checkout` command was replaced by `git switch` in git v2.23 as `git checkout` was apparently too overloaded. - **Rebasing:** essentially takes a set of commits, "copies" them, and plops them down somewhere else. - if you have 2 parallel branches and you rebase 1 onto the other it essentially looks for their common origin point (commit), takes the "base" of that branch and places the base at the end and right on top of branch you're Rebasing. - Then the branch that was the recipient of the rebasing needs to be updated so you rebase that onto the branch that was just rebased. This just moves the pointer to the tip of the merged branches - Interactive Rebasing with `git rebase -i <commit hash>` make it a more interactive way to move around and reorder commits - **HEAD** is simply the name of the currently checked out commit hash - each commit is a pointer to the repo at a point in time - each branch is a named pointer to a particular commit - **Relative References** using `^` and `~` to traverse commits in the `git log` so that you don't need to type even the minimum 6 characters of the commit hash. - `^` when given the name of a commit hash/branch name it will refer to the parent commit: `main^` - These can be stacked too so you can refer to grandparent commit with `main==` - can also use with **HEAD** to traverse from current point: `git checkout HEAD^` - `~` will let you specify a number of commits to travel back instead of `git checkout HEAD====` you can use `git checkout HEAD~4` to traverse 4 commits backward - **Forcing Branches** You can move what commit a branch is referencing with something like: - `git branch -f myBranch locationToMoveItTo` - **Reversing Commits** There are at least 2 ways to do this `git reset` and `git revert` - `git reset` move the branch backwards in time - `git revert` move the branch backwards in time and acts like the commit never even happened - `git cherry-pick <commit hash> <commmit hash> ...` - `git cherry-pick c2 c4` - **Tagging** To tag commits as a project milestone or something the commands are very simple - `git tag v1 <commit hash>` will tag what ever referenced commit hash with the tag _"v1"_ - `git tag v1` will tag what ever commit hash **HEAD** is on with the tag _"v1"_ - `git describe <ref>` If no ref is provided it will just look at wherever HEAD is. This can, however, be used on Tags. - If you have a tag on the first commit of the repo such as `0.1` with a message of `initialized repository` and then ran the command `git describe` all it would tell you is something like: - _"0.1-62-g07f34f4"_ - _0.1_ for the tag - _62_ for the commits since that tag - _g07f34f4_ is the hash being described (your current one that hasn't been committed yet) ### Resources - [Official Git Docs command shorthand](https://git-scm.com/docs) - [GREAT graphical walkthrough tool for learning git](https://learngitbranching.js.org/) - [Git Alias Tips](https://www.atlassian.com/blog/git/advanced-git-aliases) - [.gitignore Templates](https://github.com/github/gitignore) - [Git Cheatsheet](https://ndpsoftware.com/git-cheatsheet.html) - [Git Book](https://git-scm.com/docs) ### Tools - https://deepsource.io/gh/tallguyjenks/PyRM/metrics - [Github Actions](https://docs.github.com/en/actions/quickstart) - [Github code art](https://codeprints.dev/) ### Tips and Tricks #### Open a new repo with a simple url - `repo.new` in your browser URL bar #### Open any repo in VSCode spaces - take any github repo URL and add `1s` before the `.com` | This | Becomes this | | ---------------------------------------- | ------------------------------------------ | | https://github.com/tallguyjenks/gruvboxr | https://github1s.com/tallguyjenks/gruvboxr | #### Open github repo in codespaces hotkey [open code spaces with a webpage hotkey](https://twitter.com/github/status/1425505817827151872) `.` #### Github Markdown - [5 Markdown Tricks](https://grantwinney.com/cool-markdown-tricks-for-github/) ```md testing this [feature][//] [//]: # (This comment won't be rendered to the visitor!) ``` ### Bare REpository `git clone --bare <url>` or `git init --bare` ### gc "Garbage Collection" - [ ] Review git gc - <https://youtu.be/kfLrSERFgQo?t=140> > "Garbage Collection" This command operates on items in the `.git/objects` directory and compresses them, removes duplicates, and improves the speed and efficiency of your git repo ```bash git gc ``` ### git grep - [ ] review git grep - <https://youtu.be/kfLrSERFgQo?t=327> ```bash git grep search-term ``` this basically runs grep but on the files in your repository ### Create Command Aliases - Pretty Log Output (_PLOG_) ```bash git config --global alias.plog "log --graph --format='%Cgreen%h %Cred%aN%Cblue%d%Creset %s %C(yellow)(%cr)%Creset'" ``` ### git log #### Log stats by commit ```bash git log --stat ``` #### See Top contributors ```bash git shortlog -sn ``` ### Open a cloned repository from a particular commit - [SO](https://stackoverflow.com/questions/3555107/git-clone-particular-version-of-remote-repository) ```bash git clone [remote_address_here] my_repo cd my_repo git reset --hard [ENTER HERE THE COMMIT HASH YOU WANT] ``` ### git stash ```bash git stash push ``` - like an array method this will create a _Box_ and put all your changes inside of it and shove that _box_ in the corner of the room and give you a clean working tree. - The _box_ is now portable and you can switch to another branch and open the _box_ there and take out all of the changes. ```bash git stash pop ``` - This opens the _box_ and applies all those stashed changes to the current working tree. - This is very useful for the situations where maybe you made a bunch of changes and you forgot to make a new branch and you're still on `master`/`main` and you want to move all those changes to the actual feature branch. ### Saving and moving changes ```bash # say you're on master, and you have changes to docs and you're about to make a commit, but you realize "oh crap, I'm still on master, I needed to put this on a feature branch!" # you can run git stash push # to basically package up all those uncommitted changes into a "box" and shove it into a corner returning to a master branch that is a mirror of remote master (CLEAN!) # then make your branch, switch to it and run git stash pop # to grab your changes and put them onto the current working branch. ``` ### git worktree ```bash git worktree add master ``` - Create a bare repo and start making new worktrees - This means that a copy of the repo files is made for each worktree at the source commit that the bare repo was made from - Worktrees make it easier to open multiple repo branches at once under a unified workspace for easy switching of work between multiple features - Doesn't lend itself to easy updating. - The bare repo doesn't `git pull` itself but the worktrees after creation can use `git pull` but this is not ideal. The bare repo is basically frozen at a single commit for all new worktrees made. #### Making work-trees - [ThePrimeagen](https://youtu.be/2uEqYw-N8uE) ```bash git clone --bare <repo url.git> <name of the folder to create> # ex: git clone --bare [email protected]:tallguyjenks/CV.git CV # makes a bare repo of my resume # there's nothing in it, none of the files from the repo just git stuff # THEN git worktree add master git worktree add test git worktree add feature # it takes the current commit at the HEAD of the repo (git pull at you're at the most recent) and this way # you're working with 3 folders basically 3 branches of the same repo but simultaneously. NO SWITCHING BACK & AND FORTH 🤯️🤯️🤯️ ``` ### Setup #### Set Global Configs ```bash git config --global user.name "John Doe" git config --global user.email [email protected] ``` #### Aliases This sets up the shortcuts so that git recognizes what you're trying to do and you can still tab complete arguments and flags in the commands ```bash git config --global alias.<alias> '<command>' # Example: git config --global alias.last 'log -1 HEAD' ``` #### Additional information - Great bash config for using git from [this thoughtbot article](https://thoughtbot.com/upcase/videos/git-customizing) ```bash # No arguments: `git status` # With arguments: acts like `git` g() { if [[ $# > 0 ]]; then git $@ else git status fi } # Complete g like git compdef g=git ``` - A great tip to update your master branch return to the original branch you left and then merge changes. this is made as a git alias. - `!git checkout master && git pull && git fetch --prune && git checkout - && git merge master` - Aliased as `mup` for _"Master Up"_ - The `!` is running it as a shell command as git aliases can only run 1 command. So this way we're still chaining commands ### Workflow #### Commit Standards - https://betterprogramming.pub/your-git-commit-history-should-read-like-a-history-book-heres-how-7f44d5df1801 ##### Basic Structure ```bash <type>[optional !][optional scope]: <description> [optional body] [optional footer(s)] ``` ##### Examples ```bash feat: allow user to keep logged in fix: messages are now serialized correctly feat(api)!: removed the old way users are handled ci(deployment): the application will now be deployed on nonlive as well ``` ### Start Repository from existing directory then push to GitHub ```bash git init git add . git commit -m 'message' git remote add origin <url.git> git push -u origin master ``` ### Git LFS - [git-lfs wiki tutorial](https://github.com/git-lfs/git-lfs/wiki/Tutorial) - [Git Large File Storage - How to Work with Big Files](https://youtu.be/uLR1RNqJ1Mw) ### Git hooks #### Create Executable script file - <https://betterprogramming.pub/your-git-commit-history-should-read-like-a-history-book-heres-how-7f44d5df1801> ```bash #!/usr/bin/env bash # the commit message file is the last remaining arg msg_file="$1" wc --chars < "$msg_file" # Check if commit message has more than 10 characters if [[ $(wc --chars < "$msg_file") -gt 10 ]]; then exit 0 fi echo "[Commit message] $( cat "$msg_file" )" echo "The commit message is to short!" exit 1 ``` ```bash chmod +x .githooks/commit-msg git config core.hooksPath .githooks # > git commit -am "abc" # [Commit message] abc # The commit message is to short! ``` ### Troubleshooting - Removing a file from git history from [this SO article](https://stackoverflow.com/questions/307828/how-do-you-fix-a-bad-merge-and-replay-your-good-commits-onto-a-fixed-merge/15729420#15729420) ### git submodules - [ ] https://git-scm.com/book/en/v2/Git-Tools-Submodules #### Add ```bash git submodule add https://github.com/repo ``` ```bash git submodule add ``` #### Closing a repo with submodules ```bash git submodule init git submodule update ``` Simpler to do and also works recursively is to just use the clone flag: ```bash git clone --recurse-submodules ``` Update all submodules in a project ```bash git submodule update --remote ```