Git: distributed version-control system (https://git-scm.com/)
Git User Manual (git means "unpleasant person" in British English slang)
file:/usr/share/doc/git/html/user-manual.html
You can download default github .gitignore files for many programming languages here:
https://github.com/github/gitignore
Practicing and Understanding Git - ohmygit.org
License file - https://choosealicense.com/
.gitignore file - https://gitignore.io
Visualizing Git concepts with D3 -
https://onlywei.github.io/explain-git-with-d3/#commit

Ref: man gittutorial, gittutorial-2, giteveryday, gitcore-tutorial

Git is simple to use. Just type git. Without any arguments, Git lists its
options and the most common subcommands.
$ git

For a complete (and somewhat daunting) list of git subcommands, type git help --all.
$ git help --all

Git stores configuration options in three separate file, which lets you scope options
to individual repositories (local), user (global), or the entire system (system):

* local /.gitconfig - repository-specific settings
* global /.gitconfig - user-specific settings
* system /etc/gitconfig - system-wide settings

View config settings:
git config --list
view config settings and the file path:
git config --list --show-origin

Git local setup:
git config --local user.name "Saravanan Dayalan"
git config --local user.email "dayalsaravanan@gmail.com"
git config --local core.editor vim
git config --local color.ui false
git config --local init.defaultBranch main

git config --local --edit
git config --local --list
git config --local --list --show-origin

Git global setup:
git config --global user.name "Saravanan Dayalan"
git config --global user.email "dayalsaravanan@gmail.com"
git config --global core.editor vim
git config --global color.ui fasle
git config --global init.defaultBranch main

git config --global --edit
git config --global --list
git config --global --list --show-origin

Git system setup:
git config --system user.name "Saravanan Dayalan"
git config --system user.email "dayalsaravanan@gmail.com"
git config --system core.editor vim
git config --system color.ui false
git config --system init.defaultBranch main

git config --system --edit
git config --system --list
git config --system --list --show-origin

Reference: https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup

# aliases examples
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.up rebase
git config --global alias.ci commit

git help config
git config --help
man git-config

Create a new repository:
git clone https://gitlab.com/dsaravanan/Books.git
cd Books
touch README.md
git add README.md
git commit -m "add README"
git push -u origin main

Existing folder:
cd existing folder
git init
git remote add origin https://gitlab.com/dsaravanan/Books.git
git add .
git commit -m "Initial commit"
git push -u origin main

Existing Git repository:
cd existing repo
git remote rename origin old-origin
git remote add origin https://gitlab.com/dsaravanan/Books.git
git push -u origin --all
git push -u origin --tags

# git pull example (git pull does a git fetch followed by a git merge)
git init .
git remote add origin https://gitlab.com/dsaravanan/dotfiles.git
git pull origin main # download all files & commits & overrides in existing repository
git add <filename>
git commit -m <commit message>
git push -u origin main
git status
git log | less / git log --stat | less
git log --pretty=oneline    # print the git log messages in oneline

git branch
git branch <localbranchname>
git remote -v # list the web address of the repository
git rev-list @ -- file_name  # list commit message of that particular file
                               and No. of git commits that touched the file

for file in *.pdf; do echo -n "$file   "; git rev-list @ -- $file | wc -l; done

git pull origin main # to update the repository
git reflog             # manage reflog information
git diff hash1 hash2   # display difference between hash1 and hash2
git status             # to check the status of the remote repository
git rm <filename>      # delete/remove filename
git rm -f <filename>   # force delete/remove filename
git pull origin main --allow-unrelated-histories

# Delete the repository in Github
https://help.github.com/en/github/administering-a-repository/deleting-a-repository

# change git remote url
git remote set-url <remote_name> <remote_url>
git remote set-url origin https://git-repo/new-repository.git
git remote set-url origin git@github.com:user/repository.git

# remove files from a git repository
To remove a file both from the Git repository and the filesystem, you can use
git rm without any parameters (except for the file's name, of course):
$ git rm <filename>

If you only want to remove the file from the repository, but keep it on the
filesystem, you can add the --cached flag:
$ git rm <filename> --cached

When trying to delete multiple files in a directory or via a glob pattern, you
might want to perform a "dry-run" first and see which files would be removed:
$ git rm <directoryname>/* --dry-run
rm '<directoryname>/about.txt'
rm '<directoryname>/general.txt'

# remove directory from a git repository
$ git rm -r directory/
$ git commit -m 'remove directory'
$ git push origin main

# Commonly used git commands:
add         Add file contents to the index
bisect      Find the change that introduced a bug by binary search
branch      List, create, or delete branches
checkout    Checkout and switch to a branch
clone       Clone a repository into a new directory
commit      Record changes to the repository
diff        Show changes between commits, the commit and working trees, etc.
fetch       Download objects and refs from another repository
grep        Print lines matching a pattern
init        Create an empty git repository or reinitialize an existing one
log         Show commit logs
merge       Join two or more development histories
mv          Move or rename a file, a directory, or a symlink
pull        Fetch from and merge with another repository or a local branch
push        Update remote refs along with associated objects
rebase      Forward-port local commits to the updated upstream head
reset       Reset current HEAD to the specified state
rm          Remove files from the working tree and from the index
show        Show various types of objects
status      Show the working tree status
tag         Create, list, delete, or verify a tag object signed with GPG


# Practical git commands:
1. Configure User Profile
$ git config user.name "USERNAME"
$ git config user.email "user@example.com"
Add the --global option to set these policies globally.
$ git config --global user.name "USERNAME"
$ git config --global user.email "user@example.com"
2. Initialize Git Repositories
$ git init
3. Add Project Files
$ git add <file>
$ git add *.php
4. Verify Added Files
$ git status
5. Commit Changes to Repository
$ git commit  # invoke default Linux editor
$ git commit -m "commit message"
6. Display the Logs
$ git log # generalized information
$ git log <filename> # specific file information
7. Verify Project Branches
$ git branch # the output will mark the current branch with an asterisk
8. Reset Project Branches
$ git reset
$ git reset --soft
$ git reset --hard
9. Add a New Branch
$ git branch experimental # create a branch named experimental
10. Switch between Branches
$ git checkout experimental
11. Delete a Project Branch
$ git checkout main # switch to main branch
$ git branch -D experimental # delete experimental branch
12. Check Differences among Commits, Trees, and Files
$ git diff
$ git diff experimental main
13. Merge Two Branches
$ git merge experimental2 experimental1
$ git merge -s ours experimental # merges experimental branch to current development branch
$ git merge --no-commit experimental # merges branch experimental to current branch
14. Revert Existing Commits
$ git revert <commit id> # revert changes introduced by <commit id>
$ git revert HEAD~3 # relapses the fourth last commit in HEAD and performs a new commit
15. Stash Working Directory
$ git stash
$ git stash list
16. Clone a Repository
$ git clone git://example.com/git.git
$ git clone git://example.com/git.git/ test-dir/ # will download said project into test-dir/
17. Pull New Updates
$ git pull
18. Push Your Updates
$ git push
19. Display Remote Repositories
$ git remote
$ git remote --verbose
20. Connect to Remote Repositories
$ git remote add origin <server> # add 'origin' as the remote name to the server
21. Add Tags to Your Project
$ git tag 1.0.0 <commit id>
$ git log
$ git push origin --tags
22. Fetch Remote Data
$ git fetch origin
23. Restore Non-Committed Changes
$ git restore --staged test.php
$ git restore --source=HEAD --staged --worktree test.php
24. Remove Files
$ git rm *.php
$ git rm -r dir/
$ git rm --cached *.php # remove file from repository, not on the filesystem
25. Move or Rename Files
$ git mv test.py newtest.py
$ mv test.py newtest.py
$ git add newtest.py
$ rm test.py
26. Clean Untracked Files
$ git clean
$ git clean -n # options -f, -i, -n
27. Optimize Local Repositories
$ git gc
28. Archive Local Repositories
$ git archive --output=test
29. Search for Patterns
$ git grep -iw 'import' main
$ git grep 'import' $(git rev-list --all)
30. Manage Working Trees
$ git worktree list
$ git worktree add new-branch
$ git worktree remove new-branch
$ git worktree prune
31. Prune Untracked Objects
$ git prune --dry-run
$ git prune --verbose --progress
32. Pack Unpacked Objects
$ git repack
33. List Unpacked Objects
$ git count-objects
34. Validate the Object Database
$ git fsck
35. Display Changes for Each Commit
$ git whatchanged
36. Summarize Log Information
$ git shortlog
$ git shortlog --email --summary
37. Manage Cnfiguration Options
$ git config --list
$ git config --help
38. Consult Git Help
$ git help
$ git --help
$ git <command> --help
$ git commit --help
39. Consult Manual Page
$ man git
$ man git commit
40. Display Version Information
$ git --version

# To remove remote:
git remote remove origin
# Instead of removing and re-adding:
git remote set-url origin git://new.url

# To remove files from staging area:(to reverse git add <filename>)
git reset <filename>
git reset # reset all the files from the staging area

# To list all deleted files
git log --diff-filter=D --summary
git log --diff-filter=D --summary | grep delete

# Git commands that can help resolve merge conflicts
## General tools:
git status      % The status command is in frequent use when a working with Git and
                  during a merge it will help identify conflicted files.
git log --merge % Passing the --merge argument to the git log command will produce a
                 log with a list of commits that conflict between the merging branches.
git diff        % diff helps find differences between states of a repository/files.
                  This is useful in predicting and preventing merge conflicts.

## Tools for when git fails to start a merge
git checkout % checkout can be used for undoing changes to files,
               or for changing branches
git reset --mixed % reset can be used to undo changes to the working directory
                    and staging

## Tools for when git conflicts arise during a merge
git merge --abort % Executing git merge with the --abort option will exit from the
                    merge process and return the branch to the state before the merge
                    began.
git reset         % Git reset can be used during a merge conflict to reset conflicted
                    files to a know good state

# gitlab reference
https://gitlab.com/pages/plain-html
https://about.gitlab.com/stages-devops-lifecycle/pages/
https://about.gitlab.com/blog/2016/04/07/gitlab-pages-setup/
https://gitlab.com/html-themes/
https://www.atlassian.com/git/tutorials/

# downloading raw file
https://github.com/<username>/<repo-name>/<some-directory>/<filename> # github page link
wget https://raw.github.com/<username>/<repo-name>/<branch-name>/<some-directory>/<filename>
curl -OL https://raw.githubusercontent.com/<username>/<repo-name>/<branch-name>/path/to/file
O means that curl downloads the content
L means that curl follows the redirection

# fatal: refusing to merge unrelated histories
git pull origin main --allow-unrelated-histories

# 7 Git tricks
1. Autocorrection in Git (enable git autocorrection in git configuration)
$ git config --global help.autocorrect 1
If you want this to apply only to your current repository, omit the --global option.
2. Count your commits
$ git rev-list --count <branch_name>
3. Optimize your repo
$ git gc --prune=now --aggressive
This command is an internal utility that cleans up unreachable or orphaned git
objects in your repository.
4. Take a backup of untracked files
Git, along with some Bash command piping, makes it easy to create a zip archive
for your untracked files.
$ git ls-files --others --exclude-standard -z | xargs -0 tar rvf ~/backup_untracked.zip
The above command makes an archive (and exclude files listed in .gitignore).
5. Know your .git folder
Current state of HEAD:
$ cat .git/HEAD
Description of your repository:
$ cat .git/description
6. View a file of another branch
Suppose you have a file called README.md, and it's in the "main" branch. You're
working on a branch called "dev". With the following git command, you can do it
from the terminal.
$ git show main:README.md
7. Search in Git
$ git rev-list --all | xargs git grep -F ' '
$ git rev-list --all | xargs git grep -F 'font-size: 52 px;'

# ssh-key setup (~/.ssh/)
ssh-keygen -t ed25519 -C "gitlab"
xclip -i -selection clipboard < id_gitlab.pub
ssh -T git@gitlab.com
eval $(ssh-agent -s)
ssh-add /home/saran/.ssh/id_gitlab
ssh -T git@gitlab.com
Ref: https://docs.gitlab.com/ee/ssh/#working-with-non-default-ssh-key-pair-paths

ssh-keygen -t ed25519 -C "github"
xclip -i -selection clipboard < id_github.pub
ssh -T git@github.com
eval $(ssh-agent -s)
ssh-add /home/saran/.ssh/id_github
ssh -T git@github.com
Ref: https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/
generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent

ssh-keygen -t ed25519 -C "bitbucket"
xclip -i -selection clipboard < id_bitbucket.pub
ssh -T git@bitbucket.org
eval $(ssh-agent -s)
ssh-add /home/saran/.ssh/id_bitbucket
ssh -T git@bitbucket.org

ssh-keygen -t ed25519 -C "notabug"
xclip -i -selection clipboard < id_notabug.pub
ssh -T git@notabug.org
eval $(ssh-agent -s)
ssh-add /home/saran/.ssh/id_notabug
ssh -T git@notabug.org

ssh-keygen -t ed25519 -C "codeberg"
xclip -i -selection clipboard < id_codeberg.pub
ssh -T git@codeberg.org
eval $(ssh-agent -s)
ssh-add /home/saran/.ssh/id_codeberg
ssh -T git@codeberg.org

# conflicting changes in git
1. Cleaning up the working copy:
Make sure the working copy doesn't contain conflicting changes. There are
two ways to achieve this.

a) saving local changes on a stash
To preserve the local changes, can safely store them on a stash and
available in case want them back at a later point.
$ git stash --include-untracked

b) discarding local changes
If sure that don't need them anymore, can discard local changes completely.
$ git reset --hard
If untracked/new files, will have to use the "git clean" command to get rid
of these.
$ git clean -fd

2. Pull again
After cleaned up any local changes/untracked files that would have been
overwritten, the pull will finally work.
$ git pull

# rename a local git branch
git branch -m old-name new-name # rename a local branch from another branch
git branch -a # list all -- both local and remote -- branches to verify that it has been renamed

# rename a remote git branch
1. rename a local branch by following the previous step
2. then delete the old branch and push the new one
    git push origin --delete old-name
    git push origin :old-name new-name
3. reset the upstream branch for new local branch
    git push origin -u new-name

Ref: https://www.hostinger.com/tutorials/how-to-rename-a-git-branch/

# modify commit message
git commit --amend -m 'new commit message'

# safe way to rollback to a previous state
To revert everything from the HEAD back to the commit hash, meaning it will recreate
that commit state in the working tree as if every commit after 0766c053 had been
walked back.
git revert --no-commit 0766c053..HEAD
git commit -m '<commit message>'

The --no-commit flag lets git revert all the commits at once, otherwise you'll be
prompted for a message for each commit in the range, littering your history with
unnecessary new commits.

If want to have individual commits (instead of reverting everything with one big
commit), then you can pass --no-edit instead of --no-commit, so that you don't have
to edit a commit message for each reversion.
git revert --no-edit 0766c053..HEAD

# to view what haven't git added yet
git diff file.txt
# to view already added changes
git diff --cached file.txt
git diff --staged file.txt

# force git pull to overwrite local files
No need to fetch all remotes and branches if you're going to reset to the origin/main
branch. So, instead of doing:
git fetch --all
git reset --hard origin/main
I'd advise doing the following:
git fetch origin main
git reset --hard origin/main

# add & commit and file
(works only for already tracked files but not for untracked files)
git commit -am 'commit message'

# add subject and description to a commit message
git commit -m 'subject' -m 'description ...'

# fine grained control files to skip with .gitignore
ignore all .a files: *.a
but do track lib.a, even though you're ignoring .a files above: !lib.a
only ignore the TODO file in the current directory, not subdir/TODO: /TODO
ignore all files in any directory named "build": build/
ignore doc/notes.txt, but not doc/server/arch.txt: doc/*.txt
ignore all .pdf files in the doc/ directory and any of its subdirectories: doc/**/*.pdf

# git rename a file/directory and retain the history
Execute the following commands where the project .git/ directory present and also make
sure all the changes are commited.

Example:
nmsc/Scripts/Python/Euler/
then cd nmsc/
ls
.git/ Scripts/

1. rename innermost directory path
from Scripts/Python/Euler/ to Scripts/Python/eulerscheme/
git filter-repo --path-rename Scripts/Python/Euler/:Scripts/Python/eulerscheme/ --force

2. then rename previous innermost directory path
from Scripts/Python/ to Scripts/python/
git filter-repo --path-rename Scripts/Python/:Scripts/python/ --force

3. finally rename the outermost directory path from Scripts/ to scripts/
git filter-repo --path-rename Scripts/:scripts/ --force

then, 'git pull --rebase origin <branch>' before 'git push origin <branch>'

# git pull merge/rebase/fast-forward
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint:   git config pull.rebase false  # merge
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.

# add all modified files
git add -u

# git bare directory (to maintain dotfiles)
git init --bare                     # initialize bare git repository
git init --bare $HOME/.dotfiles
echo "alias config='/usr/bin/git --git-dir=$HOME/.dotfiles --work-tree=$HOME'" >> $HOME/.bashrc
config config --local status.showUntrackedFiles no
$ cd $HOME
$ config add .bashrc
$ config commit -m 'add bash configuration file'
$ config push

# using git restore to undo git add
Undoing a git add can done simply by the git restore --staged command on the
attected file:
$ git restore --staged file.txt
This will remove the file from Git's staging area, making sure it is not part of
the next commit.
If, at the same time, you also want to discard any local changes in this file,
you can simply omit the --staged flag:
$ git restore file.txt
This will undo any modifications in this file since you last committed it.
Please be careful with this command: undoing uncommitted local changes cannot be
undone!
Ref: https://www.git-tower.com/learn/git/faq/undo-git-add

# combine git repositories with unrelated histories
To merge repositoryA into repositoryB:
$ cd path/to/repositoryB
$ git remote add repositoryA /path/to/repositoryA
$ git fetch repositoryA --tags
$ git merge --allow-unrelated-histories repositoryA/main
$ git remote remove repositoryA

In case you want to put repositoryA into a subdirectory, you can use
git-filter-repo (filter-branch is discouraged). Run the following commands
before the commands above:
$ cd path/to/repositoryA
$ git filter-repo --force --to-subdirectory-filter repositoryA
Note: In above command only commited files are taken, the uncommited files are
not taken even if it present in the repositoryA. A seperate directory with
repositoryA is created within the existing repositoryA directory.

Example:
To merge julia/ into a subdirectory in codelearn/:
$ cd ~/julia/
$ git filter-repo --force --to-subdirectory-filter julia
$ cd ~/codelearn/
$ git remote add julia ~/julia
$ git fetch julia --tags
$ git merge --allow-unrelated-histories julia/main
$ git remote remove julia

Reference:
stackoverflow.com/questions/1425892/how-do-you-merge-two-git-repositories
https://jeffkreeftmeijer.com/git-combine/

# create commit with different date
$ GIT_AUTHOR_DATE='Mon May 18 19:32:10 2020 +0530' \
    GIT_COMMITTER_DATE='Mon May 18 19:32:10 2020 +0530' \
    git commit -m 'Commit message'

# add multi-line git commit messages
$ git commit program.c -m "Note the Changes" -m "- Fixed a bug"

# rename the master branch to main
renaming the local master branch to main:
The first step is to rename the "master" branch in your local git repositories
$ git branch -m master main
Let's quickly check if this has worked as expected
$ git status
On branch main
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

renaming the remote master branch as well:
Make sure your current local HEAD branch is still "main" when executing the
following command
$ git push -u origin main
We now have a new branch on the remote named "main". Let's go on and remove the
old "master" branch on the remote
$ git push origin --delete master
[Reference: https://www.git-tower.com/learn/git/faq/git-rename-master-to-main/]

# create a pull request
When you want to work on a GitHub project (https://github.com/rrthomas/pdfjam.git),
the first step is to fork a repo. This creates a new copy of pdfjam repo under your
GitHub user account with a URL like:
    https://github.com/dsarvan/pdfjam

The copy includes all the code, branches, and commits from the original repo. Next
clone the repo by opening the terminal and running the command:
    git clone https://github.com/dsarvan/pdfjam

Once the repo is cloned, you need to do two things:
1. Create a new branch by issuing the command:
    git checkout -b refactor/pdfjam
2. Create a new remote for the upstream repo with the command:
    git remote add upstream https://github.com/rrthomas/pdfjam

In this case, upstream repo refers to the original repo you created your fork from.
Now you can make changes to the code. The following code push it to refactor/pdfjam
branch.
    git push -u origin refactor/pdfjam

Once you push the changes to your repo, the 'Compare & pull request' button will
appear in rrthomas/pdfjam Github page. Click it and you'll be taken to 'Open a
pull request'. Open a pull request by clicking the 'Create pull request' button.
This allows the repo's maintainers to review your contribution. From here, they
can merge it if it is good, or they may ask you to make some changes.

Reference: https://opensource.com/article/19/7/create-pull-request-github

# git branch naming practices
Prefixing your branch names with a type helps categorize branches and provides
additional context to everyone working on the project. Some commonly used types
include bugfix/, develop/, feature/, hotfix/, main/, refactor/, release/.

* develop/ is the branch you'll be doing most of your work off of; it's also
  the branch that represents the code to be deployed in the next release.

* feature/ branches represent non-trivial features and fixes that have not yet
  been deployed (a completed feature/ branch is merged back into develop/).

* updating main/ is done through the creation of a release/.

* main/ is always 'production ready' code. Commits are never made directly to
  main/. Rather, code on main/ only gets there after a production release/
  branch is created and finished. Thus the code on main/ is always able to be
  released to production. Also, main/ is always in a predictable state, so you
  never need to worry if main/ (and thus production) has changes one of your
  other branches doesn't.

* develop/ branch is where most of your work is done. This branch contains all
  of the completed features and bug fixes yet to be released; nightly builds or
  continuous integration servers should target develop, as it represents the
  code that will be included in the next release. For one-off commits, feel free
  to commit to develop/ directly.

* for larger features, a feature/ branch should be created. feature/ branches
  are created off of develop/. They can be small enhancements for the next
  release or further out changes that, nonetheless, need to be worked on now. To
  start work on a new feature, use:

    $ git flow feature start <feature name>

  This creates a new branch, feature/<feature name>. Commits are then made to
  this branch as normal. When the feature is complete and ready to be released
  to production, it should be merged back into develop/ using the following
  command:

    $ git flow feature finish <feature name>

  This merges the code into develop/ and deletes the feature/<feature name>
  branch.

* release/ branch is created from develop/ when you're ready to begin a
  production release. Create one using the following command:

    $ git flow release start <release number>

  Note that this is the first time a version number for the release is created.
  All completed and ready to be released features must already be on develop/
  (and thus feature finished). After your release branch is created, release
  your code. Any small bug fixes needed after the release are made directly to
  the release/<release number> branch. Once it has settled down and no more bug
  fixes seem necessary, run the following command:

    $ git flow release finish <release number>

  This merges your release/<release number> changes back into both main/ and
  develop/, meaning you never need to worry about either of those branches
  lacking changes that are in production (perhaps as the result of a quick bug
  fix).

* while potentially useful, hotfix/ branches are, I would guess, little used in
  the real world. hotfix/ is like a feature/ branch off of main/: if you've
  already closed a release/ branch but realize there are vital changes that need
  to be released, create a hotfix/ branch off of main/ (at the tag created
  during $ git flow release finish <release number>) like so:

    $ git flow hotfix start <release number>

  After you make your changes and bump your version number, finalize the hotfix/
  via

    $ git flow hotfix finish <release number>

  This, like a release/ branch (since it essentially is a type of release
  branch), commits the changes to both main/ and develop/.

  The reason I assume they're rarely used is because there is already a
  mechanism for making changes to released code: committing to an un-finished
  release branch. Sure, in the beginning, teams may git flow release finish ...
  too early, only to find they need to make some quick changes the next day.
  Over time, though, they'll settle on a reasonable amount of time for a
  release/ branch to remain open and, thus, won't have a need for hotfix/
  branches. The only other time you would need a hotfix/ branch is if you needed
  a new 'feature' in production immediately, without picking up the changes
  already in develop/. That strikes me as something that happens (hopefully)
  very rarely.

# git commit template
A properly formed Git commit subject line should always be able to complete
the following sentence:
  * If applied, this commit <will your subject line here>

** Example:
[type](optional scope): [subject]

[optional body]

[optional footer]

** Type
Must be one of the following:
 * build - Build related changes
 * chore - Build process or auxiliary tool changes
 * docs - Documentation only changes
 * feat - A new feature
 * fix - A bug fix
 * perf - A code change that improves performance
 * refactor - A code change that neither fixes a bug or adds a feature
 * revert - Reverting things
 * style - Markup, white-space, formatting, missing semi-colons...
 * test - Adding missing tests

** Subject
The subject contains a succint description of the change:
 * Use the imperative, present tense: "change" not "changed" nor "changes"
 * No dot (.) at the end.

** Scope
A scope may be provided to a commit’s type, to provide additional contextual
information and is contained within parenthesis.
  e.g., feat(parser): add ability to parse arrays

** Body
Just as in the subject, use the imperative, present tense: "change" not "changed"
nor "changes".

The body should include the motivation for the change and contrast this with previous
behavior.

** Rules
The 7 rules of a great commit message
 1. Separate subject from body with a blank line
 2. Limit the subject line to 50 characters
 3. Summary in present tense. Not capitalized
 4. Do not end the subject line with a period
 5. Use the imperative mood in the subject line
 6. Wrap the body at 72 characters
 7. Use the body to explain what and why vs. how

# merge develop branch into main
To merge develop branch into the main branch - you checkout main and merge develop:
$ git checkout develop
# ...develop some code...
$ git add .
$ git commit -m 'some commit message'
$ git checkout main
# ...switched to branch main...
$ git merge develop
Reference: https://stackabuse.com/git-merge-branch-into-master/

# undo latest local commit
Let's create a file we'd like to add to our repository, add it and finally commit:

$ echo "Hello World!" >> file.txt
$ git add file.txt
$ git commit -m 'added file.txt'
$ echo "It's a great day today :(" >> file.txt
$ git add file.txt
$ git commit -m 'modified file.txt'

Yikes, we've accidentally left in a horrible typo in the content. We've put in
the parentheses in the wrong way! And we've just committed that mistake:

$ git log --pretty=oneline
df9fc1b773ecf3ba14c990615831f1087817611f (HEAD -> main) Modified file.txt
55db4f399d1ad64e0a40e1858d23fef0ffe31fb0 Added file.txt

To remove a local commit, assuming it hasn't been pushed to the remote repository yet,
we can use the `git reset` command, which is effectively the opposite of `git add`:

$ git reset HEAD~
Unstaged changes after reset:
M       file.txt

We've reset the HEAD (pointer to the last commit), pointing it back (~) to the
previous commit. By including a number after the tilde, we could've gone back
multiple commits instead of just one.

Once reset, the change we've made to the file.txt, i.e. the erroneous modification is
unstaged. Let's take a look at the log again:

$ git log --pretty=oneline
55db4f399d1ad64e0a40e1858d23fef0ffe31fb0 (HEAD -> main) Added file.txt

And the status is:

$ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   file.txt

no changes added to commit (use "git add" and/or "git commit -a")

However, what are the contents of the file now?

$ nano file.txt

Hello World
It's a great day today :(

By default, the reset command is --soft. The --soft flag doesn't reset the changes
done to the file, just removes the commit that was made. Let's go ahead and re-commit
this mistake again, so we can take a look at what happens when we run the --hard
option.

Hard Reset:
Instead of the --soft reset, which we can use to simply undo the commit, while leaving
the files intact (and the changes still present, which is why the git status command
prompted us with staging the changes) - we can also do a --hard reset:

$ git reset --hard HEAD~
HEAD is now at 55db4f3 Added file.txt

This time around, the changes aren't unstaged, like before. They're removed. If we
check the log, it'll look much like last time:

$ git log --pretty=oneline
55db4f399d1ad64e0a40e1858d23fef0ffe31fb0 (HEAD -> main) Added file.txt

Though, if we check the status:

$ git status
On branch main
nothing to commit, working tree clean

There's nothing to commit, because the change that was made to the file in the
previous commit was also removed, setting the HEAD back to the first commit:

$ nano file.txt
Hello World!

Reference: https://stackabuse.com/git-undo-latest-local-commit/

# adding multi-line commit messages
$ git commit index.js -m "My Changes" -m "- Fixed a critical bug" -m "- Probably
  added more bugs"

These multiple messages will then look like this in the resulting commit:

My Changes
- Fixed a critical bug
- Probably added more bugs

Another option, which depends on the shell you're using, is to just enter a single or
double quote and press Enter, without closing the quote. This works well in Bash,
which doesn't enter the command until you've closed the quote:

$ git commit index.js -m "My Changes
- Fixed a critical bug
- Probably added more bugs
"

And finally, you don't actually need to use the -m flag at all. If you omit this flag
then Git will automatically open a text editor for you to enter the commit message.

# remove file from remote repository without deleting it from local filesystem
git rm file.txt # remove file from remote repository and the local filesystem
git rm --cached file.txt # remove file from remote repository not the local filesystem
git rm --cached -r directory/ # remove directory from remote repository only
git commit -m 'commit message'

# git lfs

Remove Git LFS paths from Git Attributes:
git lfs --version
git lfs ls-files

git lfs track "*.pdf"
git lfs untrack "*.pdf"

Move git lfs tracked files under regular git:
git lfs untrack filename.pdf
git add --renormalize "filename.pdf"    (git add --renormalize .)
git commit -m 'restore file contents that were previously in lfs'


Reference:
https://git-lfs.com/
https://www.atlassian.com/git/tutorials/git-lfs
