GIT

 GIT -> definition -> open source distributed version control system -> tracks changes to files (but is used mostly to source code, in software development environment) -> mechanics -> GIT INIT -> starts tracking the changes in a folder -> effectively creates a new git project (called a repository) -> creates the .git HIDDEN FOLDER -> contains all the necessary tools to track the project -> contains the whole repository -> this is the folder copied from a remote source when cloning -> when cloning, only the main branch is copied, and it is set to track the main branch of the remote repo -> when a repo is cloned, Git automatically adds a remote relationship with the original remote repo, called "origin" -> GIT CLONE ['uri'] -> clones a remote repository -> GIT ADD ['file'] -> adds the files (untracked) or modifications (tracked) to the INDEX -> conceptually, it is a TREE -> when a commit is made, the current form of the index is saved (snapshot) and the local repository is updated -> also known as the STAGING AREA -> OPTIONS -> -n / --dry-run -> do not perform the ADD operation, just show what files would be ADDED -> -v -> verbose -> GIT COMMIT -> commits the changes from the index, to the repository -> each commit creates a new snapshot (version) of the repository -> OPTIONS -> -a -> automatically stages all modifications, effectively skipping the staging area step -> -m "message" -> adds a message to the commit -> --amend --author [User Name ] -> allows changing the author of the last commit -> GIT LOG -> shows the history of the repository on current branch -> the first commits displayed are the latest (reverse chronological order) -> OPTIONS -> -1 -> any number -> limit the number of commits displayed -> --oneline -> shows the commits in a one line shortened version -> displays the first digits of the SHA-1 hash, refs and commit message -> --stat -> shows more details about the modifications in each commit -> --patch -> shows the full diff for each commit -> GIT STATUS -> shows the modifications from the working tree (working directory), not yet commited to the repository (modifications to the tracked files, or adding new, untracked files) -> shows only for the current branch -> shows if in detached HEAD state -> shows if the current branch is in sync with the ORIGIN repo branch -> OPTIONS -> -s / --short -> shows a shortened version of output -> "M" (modified) is a modified file, which may be staged or not. "??" (unknown) is an unstaged untracked file. "A" (added) is a staged untracked file -> GIT DIFF [commit] [commit] -> with no arguments, shows the differences between the last commit and the working directory -> shows the differences between two commits -> commits can be identified by their hexadecimal strings of characters ids -> output -> diff --git a/test.py b/test.py			| diff --git marks the beginning of a new comparison. Next, are the compared files, called a and b and then names of the actual files index 30f664c..5447a65 100644			| files metadata (file hashes, mode identifier) --- a/test.py							| what happened to file a in this case lines were removed (---) | a line changed is interpreted as a removal of the existing line +++ b/test.py							| what happened to file b, in this case lines were added (+++)	| followed by the addition of the newline @@ -423,4 +423,4 @@ def step6(self):	| where the changes occured. -423,4 means showing 4 lines (,4 - for context) beginning with line 423 return True					| |															 # this line is added for test			| -# hello 2								| - line represents a removed line \ No newline at end of file				| +# hello 23								| + line represents an added line \ No newline at end of file				| -> git tracks file contents using hashes of their contents. If two files have the same content hashes, git might interpret that as a file rename. In this case --no-renames should be used. -> OPTIONS -> --staged -> shows the differences between the last commit and the index -> --no-renames -> turns off rename detection -> GIT CHECKOUT [commit | branch | file] -> definition -> "updates files in the working tree to match the version in the index or the specified tree" -> its purpose is to update the files in the working directory -> they are updated to match the version in the index -> by specifying a branch, checkout will update all the files to reflect the versions in that tree, effectively switching branches -> used ->on commit -> effectively moves the HEAD back to the specified commit -> the working directory is moved into a "detached HEAD state" -> it happens when a single commit is checked out -> it means that we are no longer on a branch -> any modifications (commits) to the project does not impact any of the branches -> solution -> discard changes made in this state -> simply checkout to a branch -> the commits made in this state will be garbage collected after a while -> keep the changes made -> create a branch for the detached HEAD state -> maybe merge the branch to master -> on file -> will update the file to the version in the index -> on branch -> all changes must be staged or commited before the checkout -> switches to specified branch -> the working directory will be updated to reflect the branch -> the new commits will be recordedon the current branch -> OPTIONS -> -b [branch] -> creates the branch and switches to it																					-> all unstaged/uncommited changes existing will be moved to the new branch -> this is used for "quickfixes" -> quick modifications on a branch that extend so much that more work is needed, and at the same time, the branch has to stay free -> the already made changes can be checked out to a branch -> the previous branch will be restored to the last commit -f [branch] -> force the checkout even though there ure uncommited changes in which case discards the changes. -> GIT REMOTE ADD ORIGIN ['uri'] -> GIT PUSH [remote] [branch] -> pushes the changes upstream, towards the remote repository -> [remote] is the upstream remote repository, [branch] is the branch we push to											  -> OPTIONS -> -u -> sets the upstream branch -f -> force a push -> the remote repo will be updated regardless of the conflicts that may appear -> GIT RM [file] -> the file is no longer tracked by git and is deleted -> OPTIONS -> --cached -> the file is no longer tracked by git but it is NOT deleted -> GIT MV [file] [new name] -> renames the file -> GIT BRANCH [name] -> creates a branch -> without any argument supplied, lists local-only branches -> OPTIONS -> -m [oldbranch] [newbranch] -> renames oldbranch to newbranch -> if oldbranch is not supplied, then it renames the current branch to newbranch -> -a / -all -> lists both remote and local branches, and the position of HEAD -> -d [branchname] -> deletes the target branch -> if there are unmerget commits, Git will raise an error and abort the deletion -> -D [branchname] -> forces the deletion of a branch -> GIT STASH -> stashes the working directory and staging area as WIP and clears the working directory -> OPTIONS -> list -> lists the stashed changes -> show -> shows a more detailed view of what files have been stashed -> pop -> returns the stashed changes in the working directory/index -> GIT MERGE [branch] -> merges the supplied branch into the current one -> merging a branch into another won't delete any of them -> merging creates a new commit which has both merged commits as parents and moves HEAD (and implicitly the branch pointer) to the new commit -> when merging two files a conflict may appear which must be solved manually -> if a branch has been merged with another -> one branch moves ahead having all the content from both -> the other remains as it is																				  -> if these two branches are merged again -> the first one already has all the content from both from the earlier merge -> creating a second merged commit identical to the one already existing would be wasteful -> the second branch pointer is simply moved to the merged commit -> this is called a "Fast-Forward" -> GIT REBASE [branch] -> rebases the current branch on top of the branch supplied as argument (target). -> the latest commit in the target branch becomes the BASE commit of the current (rebased) branch -> conflicts may appear and they must be solved -> the rebased commits are identical copies of the commits from the rebased branch -> GIT cannot "move" commits -> everything is identical except commit parents -> the rebased branch pointer will be moved to the target branch. -> The original commits from the rebased branch will no longer have a branch pointer and will be garbage collected -> BASE -> is the shared commit between the two branches. Effectively, is the first commit of a branch (or where the project is branching). -> base commit won't be rebased as the state it describes has already existed in the history of the project. -> REBASING vs MERGING -> both commands have the same general effect: they merge two branches -> MERGE -> keeps the project's history intact even though confusing -> it merges both branches' histories yet shows the commits as they were made. -> REBASE -> rewrites the project's history -> it appears that the changes in the rebased branch were made after the changes in the target branch, although they were made simultaneously (different timelines) -> GIT RESET ['commit'] -> moves commits from history into working directory/staging area or deletes them -> OPTIONS -> --soft -> moves the commits back into the staging area -> --mixed -> default -> moves the changes back into the working directory -> --hard -> deletes the changes from all the commits newer than the specified one -> effectively makes the specified commit the latest, and moves HEAD to it				 -> GIT PULL -> is a combination between GIT FETCH and GIT MERGE commands -> GIT FETCH -> fetches the commits from the remote repo to the local repo -> in case of a conflict, the remote branch will be separate from the local branch -> GIT TAG [tag] -> creates a tag for the current commit -> without any options, shows a list of existing tags -> OPTIONS -> -a -> creates an annotated tag -m [message] -> adds a message to the tag -> GIT SWITCH [branch] -> switches to the branch -> OPTIONS -> -c -> creates the branch and switches to it	-> PLUMBING COMMANDS -> git hash-object [object] -> calculates the SHA1 value of an object -> OPTIONS -> --stdin -> the supplied value is taken from STDIN -> git cat-file [sha] -> displays details about a git object -> OPTIONS -> -t -> displays the type of the object whose hash is supplied -> -p -> pretty prints the object -> git count-objects -> shows the number of objects in the current project -> git show-ref [reference] -> displays all the refs containing the target word in their name -> CONFIGURATION -> git --version -> displays the git version -> git config -> running git config commands -> with --global -> sets the config attribute for all the projects -> Example: git config --global user.name "Name Surname" -> without --global -> sets the config for the current project only -> Example: git config user.name "Name Surname" -> without a value -> returns the set value -> Example: git config user.name -> git config --global user.name "Name Surname" -> git config --global user.email "email@domain.com" -> git config --list -> displays the configurations -> .git STRUCTURE -> .git\objects -> folder where Git stores all its objects. -> sort of an objecy database -> first two hexa chars from the object's SHA1 hash will be the name of the subdirectory where the object is stored -> the rest of the hexa chars from the object's SHA1 hash will be the name of the file where the object is stored -> .git\info	| -> used for optimization purposes -> .git\hooks | see CONCEPT -> layer of optimization -> .git\refs\heads -> contains the branches -> .git\refs\tags -> contains the tags -> .git\refs\tags -> contains subfolders for each remote, each containing the branches of that remote -> .git\config -> git config file -> CONCEPT -> at its core Git is just a table of -> KEYS -> the SHA1 hash of the value it references -> the hash is a value which represents the content of an object, not the object itself -> VALUES -> any sequence of bytes (file contents, directories etc) -> the values are stored under these keys (are referenced by the keys) -> each object in a git repository has a SHA1 hash -> an object is a sequence of bytes. A VALUE -> each object is referenced by a KEY (SHA1 hash) -> if two objects are identical, Git will store only one and will use it everywhere it is referenced -> for example if two files have the exact same content, the trees where these files are will reference the same content blob -> a file and its content is stored -> the content in a blob and is the actual hashed value which gives the key -> the filename and permissions in the tree -> have two types -> BLOB -> it is the contents of a file stored in Git -> it is the actual data -> TREE -> a directory stored in Git -> it has a list of hashes for each object it contains -> it contains filenames which point to blobs and another trees -> COMMITS -> is just a piece of text and nothing else -> Just git cat-file on the SHA of a commit -> contains -> the commit metadata -> the hash of the parent commit. It is missing if first commit -> the hash of the content of the root directory (tree) of the project -> TAGS -> two types -> SIMPLE TAG -> it is a pointer to the commit where it was created -> a reference to a commit, just like a branch -> it is a simple name, and nothing else. -> ANNOTATED TAG -> just like the simple tag, is a pointer to the commit where it was created -> it is made of two parts -> a pointer to the hash of a BLOB -> the BLOB which contains tag metadata and another hash, of the actual commit tagged -> they are used as another way to reference a commit beside its hash -> git also has a layer of optimization which -> decides to store multiple compressed objects in the same file -> stores only the differences between files if the files are big enough and have only few differences -> a BRANCH -> is just a pointer to a commit -> points the state of the Git objects (files and directories) at a certain point (commit) -> switching to a branch -> makes HEAD point to another branch which, in turn, points to another commit. -> switching to another commit updates the objects with the state as described by the commit -> pointing to a commit determines what parent will the next commit have. Multiple commits with the same parent effectively branches the project -> HEAD -> is a pointer to the current branch -> a detached HEAD is a commit which doesn't belong to any branch -> it happens when HEAD points to a commit (hash), instead of a branch -> Git uses references between commits to track history and references between trees and blobs to track content -> references from commit to trees and blobs represents the state of the project at a given point -> REMOTE -> is a copy of the Git repository, located somewhere else, usually the cloud -> the purpose of a remote is to have the local repo synchronize with the remote one(s). -> this happens by having local branches tracking their remote counterparts -> effectively the difference in commits (how many ahead or behind is the local compared to the remote) -> the actual remote tracking -> Git looks at the local branch ref and checks what commit hash it is pointing at												  -> Git looks at the remote branch ref that the local branch is tracking and checks what commit hash it is pointing at												   -> now it can calculate how many commits ahead or behind the local repo is compared to the remote -> the repos are synchronized either by pushing or pulling -> a remote branch ref -> tracks the same branch (sequence of commits) as the local branch ref, but reflects the state of the branch (latest commit) from the remote repository -> if there is a conflict between remote and local repos, the remote branch ref will track the remote version of the branch and the local branch ref will track the local ref -> BEST PRACTICES -> before pushing changes to the remotes -> always fetch -> merge and solve conflicts -> then push -> before merging a branch with the main branch, first fetch the changes from the remote main to the local main -> never rebase shared commits -> OTHER COMMANDS -> echo "string" -> outputs the supplied string in STDOUT -> | -> pipe operator -> links commands together -> Terms: -> FORK -> a remote cone of a remote repository. -> used to work with when the original remote repo requires permissions for regular operations -> not a concept of Git, but of GitHub -> PULL REQUEST -> term used when there is a remote repo we have no permissions to write to						 -> if we want to modify it, we have to ASK the owner of the remote repo to pull changes from our cloned and modified repo -> FLOW -> TREE (index) -> modifications -> GIT ADD -> modifications added to the TREE -> GIT COMMIT -> creates a new snapshot/version of the TREE -> TREE, INDEX and STAGING AREA is the same thing -> in depth explanation -> it starts with a commited version of the tree/index (this is the version present in the repository) -> changes are made to files (the version in the working directory) -> changes that are final and deemed good are added to the tree (staged/added to the staging area/) -> adding files to the staging area can be seen as preparing a "final" version of the tree -> if the tree is what we want, a commit is made -> that version of the tree is made final with a commit (we are sure that's how we want the tree to be) -> it is also called a snapshot -> this "final for now" version has been saved and we can revert to it																														  -> the local repository is updated with this version -> else, changes are reverted/deleted and we'll work towards a better result -> PORCELAIN -> common commands used more frequently -> PLUMBING -> uncommon commands used rarely, or in scripts of some kind -> FILE STATES -> untracked -> untracked by Git -> tracked -> COMMITED -> files saved in a snapshot (git commit) -> MODIFIED -> files which have been modified from their last commited version -> STAGED -> modified files marked to be added in the next snapshot with git commit