--- tags: - notes - programming - tools author: TrudeEH draft: false showToc: true title: Version Control [GIT] --- Git is a version control system first developed by Linus Torvalds. It facilitates collaboration on large projects, keeps track of changes, and allows mistakes to be rolled back into a previous state. ## Configure Git Git uses a hierarchy of configuration files: - **System**: (`/etc/gitconfig`) Configuration for all users in a system. - **Global**: (`~/.gitconfig`) Configure Git for all project of the current user. - **Local**: (`.git/config`) Configure Git for the current project. - **Worktree**: (`.git/config.worktree`) Configure part of a project. ```Shell # Check if user name and email are set. git config --get user.name git config --get user.email # If not, set those values. git config --add --global user.name "username" git config --add --global user.email "email@example.com" git config --add --global init.defaultBranch main # GitHub's default git config --unset example.key # Remove a configuration value git config --unset-all example.key # Remove all instances of a configuration key git config --remove-section section # Remove an entire section # Rebase on pull by default to keep a linear history git config --global pull.rebase true ``` ## Create a Repository Git stores all project information in the `.git` directory. This includes branches, commits, and metadata. ```Shell mkdir project && cd project git init ``` ## Status A file can be in one of several states in a Git repository. - `untracked`: Not being tracked by Git - `staged`: Marked to be included in the next commit - `committed`: Saved to the repository's history ```Shell git status # Shows he state of a repository. ``` ## Staging Untracked files need to be indexed before being committed. ```Shell git add filename.ext # Add a file git add . # Add every file in the current directory, recursively. ``` ## Commit A commit is a snapshot of the entire repository at a given point in time. Each commit has a message that describes the changes made in that commit. To commit all staged files: ```Shell git commit -m "message" # Commit all staged files git commit --amend -m "message" # Replace the last commit's message ``` ## Git Log The `git log` command shows the history of commits in a repository. It provides information on who made a commit, when it was made, and what files were changed. Each commit also has a unique identifier (commit hash). For example, this is a valid commit hash: `46a4b5904d4ad737447052fed90c754ce8c616b6`. Since these identifiers are very long, they are often shortened to their first `7` characters (`46a4b59` in this example). ```Shell git log # Show the log in an interactive pager git --no-pager log -n 10 # Show the last 10 lines from the log, without using the pager git log -1 # Fetch the header of the first commit git log --decorate=full # Shows the full pointer to a commit git log --decorate=no # Don't show branch names git log --oneline # Show each commit in a single line. ("compact" mode) git reflog # History of actions. Commits, clone, pull, etc. ``` ## Git Diff Show differences: ```Shell git diff # Differences between the working tree and the last commit git diff HEAD~1 # Same, but includes the last commit and uncommitted changes git diff COMMIT_HASH_1 COMMIT_HASH_2 # Differences between two commits ``` ## Git Tags A tag is a name linked to a commit that doesn't move between commits. Useful to mark versions. ```Shell git tag # List the current tag git tag -a "tag name" -m "tag message" # Add a new tag git tag -a v3.10.2 -m "Fixed a lil bug" # Example marking a release ``` ### Semantic Versioning (Semver) Naming convention for versioning software. ```Plain v3.12.5 | | | | | Patch (safe bug fixes) | Minor (safe features) Major (breaking changes) ``` ## Branch A Git branch allows you to keep track of different changes separately. ```Plain D - E other_branch / A - B - C main ``` ```Shell git branch # Check which branches are available, and which one is selected git branch -m oldname newname # Rename a branch git branch my_new_branch # Create a new branch git switch -c my_new_branch # Create a new branch and switch to it immediately git switch branch_name # Switch to an existing branch git checkout branch_name # Deprecated alternative to git switch git branch -d branch_name # Delete a branch ``` ### Merge After modifying a new branch, all commits performed on it can be merged into the main branch. ```Plain A - B - C - F main \ / D - E other_branch ``` ```Shell git log --oneline --graph --all # Show an ASCII representation of the commit history git log --oneline --decorate --graph --parents # Also display any parent branches git merge branch_name # Merge a branch into the current branch ``` ### Rebase After a branch is created, it might fall behind its origin. A rebase includes the new changes from the origin into the current branch, without leaving a merge message behind. While merging can often accomplish the same task, a rebase keeps history linear and makes it easier to read. It's recommended to use it on the main branch, for example, when updating smaller ones. Before a rebase, the commit history might have the following structure: ```Plain A - B - C main \ D - E feature_branch ``` After a rebase, the target branch is updated against its origin: ```Plain A - B - C main \ D - E feature_branch ``` ```Shell git rebase origin_name # Rebase against the origin ``` > You should *never* rebase a public branch (like `main`) onto anything else, to not break commit history. ### Conflicts If a modified is pushed to another branch where that same file has been modified as well, a conflict arises. These can be fixed manually by editing text, or using Git commands. ```Shell git checkout --theirs path/to/file # Discard the current branch's changes and accept the target's changes git checkout --ours path/to/file # Discard the target changes and replace with the ones in the current branch git reset --soft HEAD~1 # Undo an accidental conflict resolution ``` ### Squash Combine various commits into one: 1. Start an interactive rebase with the command `git rebase -i HEAD~n`, where `n` is the number of commits you want to squash. 2. Git will open your default editor with a list of commits. Change the word `pick` to `squash` for all but the first commit. 3. Save and close the editor. > If the squashed commits already existed in the remote repository, it might be needed to push using: `git push origin main --force`. ### Stash `git stash` saves the state of the current working directory and the index (staging area), then returns the repository to `HEAD`. This allows you to work on a different issue, and then resume your previous task. ```Shell git stash git stash -m "message" # It's also possible to stash with a message git stash list # List all stashes git stash pop # Apply the most recent stash to the working directory git stash apply # Same as before, but doesn't delete the stash after applying git stash drop # Discard a stash without applying changes git stash apply stash@{2} # Apply the third most recent stash ``` ### Worktrees The directory where the code tracked with Git lives. (And where the `.git` directory is located). Use instead of a `stash` if the current worktree is too busy, or for long-lived changes. This acts like cloning the repo again and working there, except it doesn't take up space on the host machine. Any change done on a linked worktree is reflected on the main one instantly. Think of it as a different view of the main worktree. ```Shell git worktree list # Lists worktrees git worktree add [] # Create a worktree linked to the main one git worktree remove WORKTREE_NAME # Remove a worktree git worktree prune # Remove an empty worktree if its directories were removed ``` ### Bisect Find a specific commit with binary tree search. For example, if trying to find a bug in 100 commits, `git bisect` allows it to be found with only `7` attempts. 1. `git bisect start` 2. Use: `git bisect good ` to select a commit where the bug wasn't introduced yet. 3. Select a commit where the bug exists using: `git bisect bad `. 4. Git will checkout a commit between the good and bad commits for you to test to see if the bug is present. 5. Execute `git bisect good` or `git bisect bad`. 6. Loop back to step 4 (until `git bisect` completes) 7. Exit the bisect mode with `git bisect reset` ```Shell git bisect start git bisect good # Select a commit where the bug wasn't introduced yet. git bisect bad # Select a commit where the bug exists ``` #### Automated Bisect If you have a script that can tell if the current source code is good or bad, you can bisect by issuing the command: ```Shell git bisect run