Git /hooks: what are the hooks for other Git hooks? Git folder, what are all the files? So I wrote this article.

The.git folder is also a good place to start. Clues about how Git works can be found here.

1. .gitFolder creation

You can create a.git folder (in any folder, initialize the repository with git init. The header is a hidden folder, so it may not be seen at ordinary times. Git Repository git Repository git Repository git Repository git Repository

If, for some reason, you want to start again, rm -rf. git && git init, the git record in this repository will return to zero! (Caution: Use with caution!!)

2. .gitstructure

Git init temp, run CD temp && ls-f1.git, and you can see the basic.git directory structure:

HEAD
config
description
hooks/
info/
objects/
refs/
Copy the code

But there is no substantial content, the research is not significant. Git to see a richer.git directory structure (usually 7 files and 5 directories) :

COMMIT_EDITMSG
HEAD
ORIG_HEAD
FETCH_HEAD
config
description
index
hooks/
info/
logs/
objects/
refs/
Copy the code

Important: before starting, please make a backup of the entire warehouse!!

Important: before starting, please make a backup of the entire warehouse!!

Important: before starting, please make a backup of the entire warehouse!!

2.1 COMMIT_EDITMSG file

Git commit is a temporary file that stores the contents of the last commit. The editor that opens after the git commit command is used to edit this file, and when you exit the editor, Git will write this file to the commit record.

⭐ : after git pull, many new commits are added to the remote repository, and local commit records are drowned.

2.2 file HEAD

This file always stores the current location pointer, which, like the $PWD variable and command prompt arrows in Linux, always points to the current location, indicating the current working location. In Git, HEAD always points to the commit that is currently working.

Branch HEAD

HEAD stores a branch ref, and running: cat.git /HEAD will usually display:

ref: refs/heads/master
Copy the code

This indicates that you are currently working in the Master branch. Any commit you make at this point is automatically attached to the master branch by default.

Git cat-file -p HEAD

$ git cat-file -p HEAD
tree 95a4c1cd778ad62586c47afc06d2a1b5dff1bdec
parent bfdec30d39951b49fa8964863bd801058878f3b2
author Wu-Yikun <[email protected]> 1634876894 +0800
committer Wu-Yikun <[email protected]> 1634876894 +0800

?
Copy the code

Isolate the HEAD

Git checkout bfDEC30d git checkout bfDEC30d

You are in ‘detached HEAD’ state. You can look around, make experimental

changes and commit them, and you can discard any commits you make in this

state without impacting any branches by performing another checkout.

Git /HEAD is a file containing information that is no longer a branch. Run cat. git/HEAD and see:

$ cat .git/HEAD
bfdec30d39951b49fa8964863bd801058878f3b2
Copy the code

See the difference? HEAD points to a 40-character SHA-1 commit record. Git doesn’t know which branch you’re working on anymore, so if you make a new commit, Git doesn’t know where to push it, and you’re just doing some experimental code that doesn’t affect any branches and doesn’t work with anyone. This is what’s called the ‘detached HEAD’ state.

(Branch HEAD to isolated HEAD)

Examples of use of HEAD:

$ git push origin HEAD
$ git checkout HEAD~1
Copy the code

2.3 file ORIG_HEAD

Because HEAD is important, this file will back up the HEAD if you do something dangerous, such as:

$ git reset
$ git merge
$ git rebase
$ git pull
Copy the code

An example of this file being used:

#Rollback to previous state
$ git reset --hard ORIG_HEAD
Copy the code

2.4 file FETCH_HEAD

Git pull/fetch/merge git pull/fetch/merge

Git pull is equivalent to executing the following two commands (pull = fetch & merge) :

$ git fetch
$ git merge FETCH_HEAD

#>>>From https://github.com/xxx/xxxx * branch master -> FETCH_HEAD Updating f785638.. 59db1b2Copy the code

And, at this point, the HEAD is silently backed up to ORIG_HEAD.

Let’s see what’s in FETCH_HEAD:

$ cat .git/FETCH_HEAD
848d7701250d5fee1449c5355158f629f6564484        branch 'master' of https://github.com/xxxx/xxx
Copy the code

The hash value is first followed by the branch information that requires the FETCH.

This file may have more than one line, for example:

$ cat .git/FETCH_HEAD
848d7701250d5fee1449c5355158f629f6564484        	branch 'master' of https://github.com/xxxx/xxx
81d84ed74fc2b29c73d6ac82d681e5819b4d35d3        	branch 'next' of https://github.com/xxxx/xxx
a25f5f1615a479e717a82bc4a10d816a44de6cd1		not-for-merge   branch 'add-i18n' of https://github.com/xxxx/xxx
065c1b268386d533be65f4ae34742b2f1780d589        not-for-merge   branch 'add-sche-catch' of https://github.com/xxxx/xxx
Copy the code

Git pull: merge git pull: fetch + merge ⭐ : git pull: fetch + merge

This feature was added after git 2.5 in 2015. When git pulls, not-for-merge is used as a magic string to determine whether to merge from a remote branch to a local branch. The above source code comment is perfectly written:

“Appends merge candidates from FETCH_HEAD that are not marked not-for-merge into merge_heads.”

2.5 the config file

This file stores git Settings that are local to your project, typically as follows:

[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true [remote "origin"] url = [email protected]/xxx.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = Origin merge = refs/heads/master [branch "v2.6.0"] remote = Origin merge = refs/heads/v2.6.0 [branch "v2.8.0"] remote = The origin of the merge = refs/heads/v2.8.0Copy the code

This is a typical INI configuration file. Each section can contain multiple variable = values, where the [core] field contains various Git parameter Settings, such as ignorecase = true to ignore filename case.

⭐git config –global ~/.gitconfig; You can run the following command to modify the file:

$ git config --global -e
Copy the code
  • [core]The content of the paragraph is similar togit configCommand corresponding to the

Run the following command:

$ git config user.name abc
$ git config user.email [email protected]
Copy the code

The following is appended to the config file:

. . [user] name = abc email = [email protected]Copy the code
  • [remote]The segment represents the remote repository configuration

Git Internals – The Refspec

  • [branch]Segments represent branch synchronization Settings

If you are currently in the master branch and execute git pull, the following message is displayed:

There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

   git pull <remote> <branch>
Copy the code

Git /config file lacks the corresponding [branch “master”] field.

Solutions:

$ git branch -u origin/master master

#Or do a push
$ git push -u origin master

#Or run the following command to associate the master of the local branch with the master of the remote branch.
$ git push --set-upstream origin master:master
Copy the code

A prompt will appear:

Branch ‘master’ set up to track remote branch ‘master’ from ‘origin’.

Git /config generates the following:

[branch "master"]
        remote = origin
        merge = refs/heads/master
Copy the code

If you manually edit.git/config, it will have the same effect. This is what upstream really means, which is to generate the configuration in config.

2.6 file description

See the following description in the document:

The description file is used only by the GitWeb program, so don’t worry about it.

This file is mainly used to describe GitWeb. If we want to start GitWeb, we can use the following command:

#Make sure lighttpd is installed: Brew Install Lighttpd
$ git instaweb --start
Copy the code

By default, the lighttpd service is started and the browser http://127.0.0.1:1234 is opened (try changing the external IP and sharing it with others?).

The current git repository name and description are displayed below. The default description is as follows:

Unnamed repository; edit this file ‘description’ to name the repository.

This is the content of the default description file. Edit this file to make your GitWeb description more user-friendly. I found no other use for it.

2.7 Folder hooks/

Save git hooks for checking before and after git commands or doing custom actions. Run ls-f1.git /hooks

Prepare-commit_source: SHA1 commit_msg. Sample # git commit: Sample # git COMMIT_EDITMSG before commit, COMMIT_EDITMSG before commit Sample # git push # git push # git push # git push Sample # git rebase = git rebase; git rebase = git rebase; git rebase = git rebase Sample # Used to verify git am submission information before git am is executed Fsmonitor -watchman.sample # works with core. Fsmonitor Settings to better monitor file changesCopy the code

Reference: git-scm.com/docs/githoo…

To enable a hook, simply delete.sample and edit its contents to implement the appropriate logic.

If you want to verify that each commit message contains at least two words, you will be prompted to reject the commit.

#! /bin/shGrep - q '\ \ S \ \ S + S $1 | | {echo' submit information for at least two words' && exit 1; }Copy the code

Git /hooks/commit -msg. git/COMMIT_EDITMSG. If the value is not 0, the commit is rejected.

2.8 Folder Info /

This folder basically has two files:

  1. fileinfo/excludeUsed to exclude rules, and.gitignoreFunctions are similar.
  2. Files may be includedinfo/refsIs used to track information about branches. This file is usually executed by commandgit update-server-infoGenerate, the contents of which:
94e1a0d952f577fe1348d828d145507d3709e11e    refs/heads/master
# object hash                                   # branch reference
Copy the code

This means that the master file hash value for the branch points to 94 e1a0d952f577fe1348d828d145507d3709e11e,

Run the git cat file – p – 94 e1a0d952f577fe1348d828d145507d3709e11e, you can see the master branch finally submit records of information.

At the same time: the git/objects / 94 / e1a0d952f577fe1348d828d145507d3709e11e can see the last of the binary file content.

The info/refs file is crucial to setting up a Git server.

2.9 Folder logs/

Git reflog commands and paths like HEAD@{1} are used to record the operation information. If you delete this folder (danger!) Commands that rely on reflog will report an error.

$ mv .git/logs .git/logs_bak
$ git checkout HEAD@{1}
Copy the code

The following error message is displayed:

error: pathspec ‘HEAD@{1}’ did not match any file(s) known to git

2.10 Folder objects/

This directory is a git database. Run tree.git /objects to see the directory structure:

.git/objects/
|-- 0c
|   `-- d370696b581c38ee01e62b148a759f80facc2d
|-- 59
|   `-- 3d5b490556791212acd5a516a37bbfa05d44dd
|-- 61
|   `-- be44eedde61d723e5761577a2b420ba0fc2794
|-- 64
|   `-- c0aed8ddcbb546bdcec2848938fc82348db227
|-- d4
|   `-- 9904676ce8ddde276bdbfa9bbec313e90e0f50
|-- info
`-- pack
    |-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.idx
    `-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.pack
Copy the code

These files come in two forms: Pack compressed files in the pack/ directory, and other hash files called loost Objects.

I can’t find a separate name for this folder and its algorithm, so let’s call it a hash-object system. Git hash-object: git hash-object: git Hash-object: git Hash-object: git Hash-object: git Hash-object

$ git cat-file --batch-check --batch-all-objects
Copy the code

You can see

04c87c65f142f33945f2f5951cf7801a32dfa240 commit 194 098217953a6ca169bed33d2be8a07d584fcdaf30 tree 31 0cd370696b581c38ee01e62b148a759f80facc2d commit 245 2a810017bfc85d7db2627f4aabdaa1583212bda3 blob 19 3920a07c1d5694df6b8658592b0939241d70e9e5 tree 93 593d5b490556791212acd5a516a37bbfa05d44dd tag 148 61be44eedde61d723e5761577a2b420ba0fc2794 tree 154 ... .Copy the code

But you’ll notice that there are some values in this list that don’t exist in the folder, because in addition to loost Objects it also aggregates the contents of the pack file.

Hash file

Also called loose Object, the file name consists of a 40-character SHA-1 hash value, in which the first two characters are buckets and the last 38 characters are file names.

There are four types of file content: commit, tree, blob, and Tag. All four types are generated if you run the following command:

$ echo -en 'xx\n' > xx  # contains three characters
$ git add .
$ git commit -m 'update xx'
$ git tag -a 'v1.0' -m ' '1.0.0 release.
Copy the code

After the above operations, compare the file tree and find four more hash files:

|-- 0c
|   `-- d370696b581c38ee01e62b148a759f80facc2d
|-- 18
|   `-- 143661f96845f11e0b4ab7312bdc0f356834ce
|-- 30
|   `-- 20feea86d222d83218eb3eb5aa9f58f73df04d
|-- 59
|   `-- 3d5b490556791212acd5a516a37bbfa05d44dd
|-- 61
|   `-- be44eedde61d723e5761577a2b420ba0fc2794
|-- 64
|   `-- c0aed8ddcbb546bdcec2848938fc82348db227
|-- ad
|   `-- f4c9afac7afae3ff3e95e6c4eefe009d547f00
|-- cc
|   `-- c9bd67dc5c467859102d53d54c5ce851273bdd
|-- d4
|   `-- 9904676ce8ddde276bdbfa9bbec313e90e0f50
|-- info
`-- pack
    |-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.idx
    `-- pack-75e3f2aa378752ec93a8e9f375f01204d498605b.pack
Copy the code

The four hash files are:

cc/c9bd67dc5c467859102d53d54c5ce851273bdd  # blob
30/20feea86d222d83218eb3eb5aa9f58f73df04d  # commit
ad/f4c9afac7afae3ff3e95e6c4eefe009d547f00  # tree
18/143661f96845f11e0b4ab7312bdc0f356834ce  # tag
Copy the code

We want to see what’s in there? In fact, these files are compressed, in the form of zlib. Brew Install Pigz for Windows or macOS

$ pigz -d < .git/objects/cc/c9bd67dc5c467859102d53d54c5ce851273bdd

#BLOB type, display result >>>>(note after xx \n)
blob 3xx
$ pigz -d < .git/objects/30/20feea86d222d83218eb3eb5aa9f58f73df04d

#If the COMMIT type is displayed, >>>> is displayed
commit 248tree adf4c9afac7afae3ff3e95e6c4eefe009d547f00
parent 0cd370696b581c38ee01e62b148a759f80facc2d
author jamesyang.yjm <[email protected]> 1562044880 +0800
committer jamesyang.yjm <[email protected]> 1562044880 +0800

update xx
$ pigz -d < .git/objects/ad/f4c9afac7afae3ff3e95e6c4eefe009d547f00

#TREE: >>>> is displayedtree 154100644 abc*??? ] }? bJ? ڡ X2?? 100644 asdf??? CK?) ? wZ??? S? 100644 iou??? CK?) ? wZ??? S? 100644 xx? ɽ g? \FxY-S? L\? Q'; ? 100644 yy??? CK?) ? wZ??? S?$ pigz -d < .git/objects/18/143661f96845f11e0b4ab7312bdc0f356834ce

#The TAG type is >>>>The tag 155 3020 feea86d222d83218eb3eb5aa9f58f73df04d object type commit tag v1.0 tagger jamesyang. Yjm <[email protected]> 1562045942 +0800 release: 1.0.0Copy the code

The format of the object file is type size+ content.

[type] [size][NULL][content]
Copy the code

Type Optional values: commit, tree, blob, tag, NULL is the C language character terminator: \0, size is the length of the content after NULL bytes.

You can use git cat-file -t hash to view the types of type and git cat-file -p hash to view the contents.

$ git cat-file -t ccc9bd67dc5c467859102d53d54c5ce851273bdd

#The command output is >>>>
blob
$ git cat-file -p ccc9bd67dc5c467859102d53d54c5ce851273bdd

#The command output is >>>>
xx
Copy the code

So a blob file is a full copy of the contents of the original file, preceded by blob size\0, and the hash value of the file name is the sha-1 value of the entire character:

$ echo -en 'blob 3\0xx\n' | shasum
#The command output is >>>>
ccc9bd67dc5c467859102d53d54c5ce851273bdd  -
Copy the code

After knowing the principle, please refer to GitMagic by Ben Lynn at Stanford for other types of formats.

So, when we git show 3020 feea86d222d83218eb3eb5aa9f58f73df04d, what will happen?

  1. find3020feea86d222d83218eb3eb5aa9f58f73df04dthiscommitCome out
  2. To find thecommitThe associatedtree object: adf4c9afac7afae3ff3e95e6c4eefe009d547f00And pull the correspondingblobFile and do with the files in the current workspacediffAnd then display it

This is a real example of the objects/ folder being used as a Git database.

Pack file

Why is there a.pack file?

Since many hash files are generated for each commit and blob files are stored in full volume, Git efficiency is reduced.

  1. High storage efficiency for large warehouse
  2. Facilitate network transmission and backup
  3. Incremental storage to optimize disk space

Pack some of the.git/ Objects files into pack format

$ tree .git/objects/ | wc -l
311

$ git gc
Enumerating objects: 288, done.
Counting objects: 100% (288/288), done.
Delta compression using up to 4 threads
Compressing objects: 100% (287/287), done.
Writing objects: 100% (288/288), done.
Total 288 (delta 131), reused 90 (delta 0)

$ tree .git/objects/ | wc -l
12
Copy the code

You can see that the number of files is much smaller, most of them are put into a.pack and stored incrementally, some of the changed files only store basic hash + changes, and the disk space optimization is obvious.

⭐** Git gc actually runs two commands: git repack for packing and git prune-packed for removing packed hash files **

  • If you want to package all the files, this is not recommended, but you can use the following command:
$ git repack -a -d -f --depth=250 --window=250
Copy the code

Specific visible: this problem

  • To see what’s in the bag, run:
$ git verify-pack -v .git/objects/pack/pack-5963b552193021791c1a0ab9136c272f07124c98.pack
Copy the code

The following information is displayed:

5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 commit 245 153 12 2305588a632214f266462260428c4395f936b5b0 commit 252 156 165 1fa9735670eb952b6468d17b418525717c8e3527 commit 248 156 321 3ffb7fb9830e232669c95b3b65f0f8f3fc7a6027 commit 248 155 477 86a5912f97d7dd8f90a28cab6bffc8ee78997e2c commit 244 151 632 94e1a0d952f577fe1348d828d145507d3709e11e commit 249 156 783 86903f8f5024485afa8480020a04cc00f228d23c commit 243 150 939 6efdffad4fb725aa8d0f4d7d29feb5aee7ea5dff commit 242 151 1089  04c87c65f142f33945f2f5951cf7801a32dfa240 commit 73 85 1240 1 6efdffad4fb725aa8d0f4d7d29feb5aee7ea5dff 2a810017bfc85d7db2627f4aabdaa1583212bda3 blob 19 27 1325 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 blob 0 9 1352 b5e810691433cf8a2960c27c1b33546fa96e2bef blob 16 26 1361 2f36e957afc2b3bcda988cb29a86e3a1490e8cc2 tree 153 106 1387 2ed6130bd33afa26817418308e29c4081ea056ec tree 5 15 1493 1 2f36e957afc2b3bcda988cb29a86e3a1490e8cc2 9df301ad27294a62ba1ae65aaed489072d778c79 tree 123 103 1508 7d48a14b9ca1dca2f6a593eef19633ce45f81bee blob 12 21 1611 a448b4d6450de854dcc6fe658bdb72e22c726cbb tree 123 102 1632 9e56fd51f52d8b9d242c50c24a4cae586d76ec7e blob 7 16 1734 bde15b851f135327ada02c9deac0fb1ee01cf343 tree 123 102 1750 58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c blob 4 13 1852 3920a07c1d5694df6b8658592b0939241d70e9e5 tree 7 17 1865 1 bde15b851f135327ada02c9deac0fb1ee01cf343 16729e3b94f19bc95cb6f563f776bfb4694a6e5b tree 4 14 1882 2 3920a07c1d5694df6b8658592b0939241d70e9e5 b72c74792528892694c395b2c9a3d6af740f3fb2 tree 63 50 1896 098217953a6ca169bed33d2be8a07d584fcdaf30 tree 31 42 1946 non delta: 20 objects chain length = 1: 3 objects chain length = 2: 1 object .git/objects/pack/pack-5963b552193021791c1a0ab9136c272f07124c98.pack: okCopy the code

The following string of numbers is detailed in the documentation:

When specifying the -v option the format used is:

        SHA-1 type size size-in-packfile offset-in-packfile

for objects that are not deltified in the pack, and

        SHA-1 type size size-in-packfile offset-in-packfile depth base-SHA-1

for objects that are deltified.
Copy the code

The last entry with hash indicates the base hash for incremental storage, preceded by the incremental depth.

2.11 Folder refs/

Refs can be interpreted as the file system symbol link.

$ tree .git/refs/. Git/refs | - heads | ` - master ` - tags ` - v1.0
$ cat .git/refs/heads/master 
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5

$The git/refs/tags/v1.0    
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5

$ git cat-file -t 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5
commit
Copy the code

You can see the master and v1.0 point to 5978 c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 the commit.

Refs /heads/ folders are typically generated through Git Branch. Git show-ref –heads

Refs /tags/ folders are usually generated using git tags. Git show-ref –tags can be viewed.

As follows:

$ git branch abc

$ tree .git/refs/. Git/refs / | - heads | | -- - | ABC ` - master ` - tags ` - v1.0
$ cat .git/refs/heads/abc 
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5
Copy the code

Creating a new branch creates a symbol link that points to a commit.

The Git tag command is called Lightweight Tag because it is similar to git Branch and only generates a ref in the tags directory.

Git tag -a xx will first generate a hash file of type tag in the objects/ directory, and then generate a ref in the tags directory pointing to that file. This tag is called annotated tag, which contains meta information such as tagger and message, is managed by Git’s Hash object algorithm, and can be signed by GPG, so it is more stable and secure.

Use the following command to retrieve the information stored in the refs folder:

$ git show-ref --head --dereference5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 HEAD 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/abc 5978 c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/master 5978 c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/v1.0 5e84371048faa20412f5492e6af264a7e1edfec1 refs/tags/xx 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/xx^{}Copy the code

Let’s look at how this information has changed:

$ touch new_file && git add . && git commit -m 'add new_file'
[master 44b0d05] add new_file
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 new_file

$ git show-ref --head --dereference44b0d05ddadaaa8d2cc40d6647cc474b26f5d8d3 HEAD 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/abc 44 b0d05ddadaaa8d2cc40d6647cc474b26f5d8d3 refs/heads/master 5978 c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/v1.0 5e84371048faa20412f5492e6af264a7e1edfec1 refs/tags/xx 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/xx^{}Copy the code

Diff can be seen below:

5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 HEAD
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/master
Copy the code

These two rows have changed. The HEAD and heads update automatically every commit.

2.12 file index

If you’re careful, there’s no index file, right? Here’s why:

The ⭐**index file is the most important part of git’s hash-object system and deserves a separate article.

You can first refer to the following articles:

  • Ruan Yifeng teacher wrote Git principle introduction 中 The staging areaPart of the
  • Git index format
  • Use of index and Racy Git problem

To recap, index is a tiny Linux file system that implements inode in the most economical way possible, which is no accident since the man who came up with the idea was Linus Torvalds, the creator of Linux.

This file is also called git Staging Area. Git add is used to create an index entry by taking some of the data captured by stat and writing it to a.git/index file. Multiple Index entries form a tree.

Git commit is to store the tree structure and bloB in the objects/ folder and generate a commit record.

Git reset is to discard the tree that was just written to the index file and restore a tree from the HEAD.

Git status compares the tree stored in the index file with the files in the workspace at the STAT level and outputs the changes.

Above, let’s summarize these folders with a picture:

📚 Reference: git-scm.com/docs/githoo… Git-scm.com/book/en/v2/… Git-scm.com/book/en/v2/… Www-cs-students.stanford.edu/~blynn/gitm… Git-scm.com/docs/git-sh… Stackoverflow.com/questions/2… Git-scm.com/docs/git-re…

We hope this article will help you 🧠 feel free to leave your thoughts in the comments 🌊, we will discuss and share 🔥