This article is part of a series on horseshoe Git, with more to come

GitHub address (constantly updated) : github.com/veedrin/hor…

Blog address (the layout is really beautiful) : matiji.cn

If you think it will help you, please come to GitHub to Star or come to my blog to tell me personally

When I first started working with Git, I had no idea why one operation used this command and another used that command.

Because Git is not a set of tools focused on user experience, git has its own philosophy. You have to understand its philosophy first to really understand how it works.

I also read an article written by my predecessor and was enlightened at a certain moment.

You’ve all heard how powerful Git is. You’ve all experienced how confusing Git can be.

To sum up, there are two ways to learn Git: first, to understand the philosophy of Git; Second, accumulate experience in dealing with problems in complex practice. Both are indispensable.

This article is the first one.

The author myself is also on the way, after all, this article is only my learning experience, still need a lot of practice.

There are many ways to write Git, and after a lot of consideration, I finally decided to write it from a command point of view without splitting the reading experience.

Due to the 20,000 word limit, the Nuggets cannot publish the full version, please go to my GitHub or personal blog to read the full version.

Difficult years, mutual encouragement.

01) add

Git is a database system, git is a content-addressing file system, and Git is a version management system.

Yes, it’s both.

Instead of obsessing over what Git is, let’s dive right into the git command.

To add untracked files and changes to tracked files to the staging area, use the git add command.

Git add is not semantic enough to add to the staging area. So git has added another command, git stage, which has exactly the same effect.

Git repositories, workspaces, and staging areas

Before getting into the subject, let’s introduce the concepts of Git repositories, workspaces, and staging areas.

Git repository

A Git repository is a folder with a. Git directory. It is where all the stories about Git begin.

You can initialize a Git repository using the git init command.

$ git init
Copy the code

You can also clone the repository locally from the server using the git clone command.

$ git clone [email protected]:veedrin/horseshoe.git
Copy the code

You then have a local Git repository that looks exactly like the one on your server.

It should be noted that the Clone operation does not download the entire repository, but only the.git directory. Because everything about Git is in this directory, git can be restored to any version of the repository.

Working Directory

The workspace, also known as the working directory, does not include the project root of the.git directory. The directory in which we will be doing our work is the repository for versioning. You can even call any work-related directory a workspace, except there isn’t.

Staging area (stage or index)

A staging area is a place where submission to a repository is prepared.

So why is it called index? Because the staging area is physically just the index binary in the.git directory. It is an index file that matches files in the workspace to backups in the staging area.

Stage is ideographic, index is table.

You can think of the staging area as a piggy bank. When we were kids, every dime we had went into a piggy bank. When the shaking of the piggy bank becomes louder, or when we have a wish and need money urgently, we break open the piggy bank and spend it all at once.

In the analogy of software development, whenever we finish writing a small module, we can put it in the staging area. Once a complete feature is developed, we can commit it to the repository once from the staging area.

The benefits of this are obvious:

  • It can achieve smaller granularity of undo.
  • It enables bulk commits to the repository.

In addition, adding to the staging area actually involves two operations. One is to put files that haven’t been tracked by Git into the staging area. One is a file that has already been tracked by Git and places the changes in the staging area.

Put it in temporary storage

Git does not place workspace files in the staging area by default.

$ git status

On branch master
No commits yet
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    a.md
nothing added to commit but untracked files present (use "git add" to track)
Copy the code

We see that the file is now labeled Untracked Files. Means that Git is currently unable to track their changes, meaning that they are not in staging yet.

So how do we manually place files or folders into the staging area?

$ git add .
Copy the code

The above command puts all files in the working directory that are not in the staging area into the staging area. In this case, the file state Changes to be committed, indicating that the file is in the staging area, waiting for the next commit. Each add operation is essentially creating a copy of the file or content that was added.

The following command can achieve the same effect.

$ git add -A
Copy the code

What if I just want to deposit a single file? Follow the file name relative to the current directory.

$ git add README.md
Copy the code

The same goes for temporary storage of entire folders. Because Git recursively stores all files in a folder.

$ git add src
Copy the code

The git add command is used to add files to the staging area that have never been marked before.

This state becomes Changes not staged for Commit.

$ git status

On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   a.md
no changes added to commit (use "git add" and/or "git commit -a")
Copy the code

There is also a command to add changes to the staging area for files already in the staging area.

$ git add -u
Copy the code

The difference between git add -a and git add -a is that it can only put changes to files that have been added to the staging area, whereas git add -a can do both.

Trace content

Assuming we have added the file to the staging area, now we add content to the file, place it in the staging area again, and view the status.

$ git status

On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   a.md
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   a.md
Copy the code

Well, suddenly this is interesting. Why is a file in two states at the same time? Is it Schrodinger’s cat?

Imagine if I wanted to fix a bug and then add a Feather to a file. I would definitely want to put it in staging in two times for granular undo and commit. But git can’t do that if it does file-based version management.

So Git can only do content based version management, not file based version management. The smallest unit of version management is called a HUNK, and a HUNK is a sequence of changes. It is not unusual for a file to have two states at once.

objects

The git project has a directory called Objects under the git directory. At the beginning, this directory has only two empty directories: info and Pack.

Once we execute git add, there will be something more under the Objects directory.

.git/
.git/objects/
.git/objects/e6/
.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Copy the code

It has a 2-character directory and a 38-character file. That adds up to exactly 40 characters. A 2-character directory was added to improve retrieval efficiency.

Sha-1 is a hash encryption algorithm that obtains the same checksum as long as the encrypted content is the same. Of course this is inaccurate, but the probability of a collision is extremely low.

Git adds some additional information in addition to the content used to calculate the checksum to further reduce the probability of collisions.

The important point is that sha-1 computes checksums based on content, just like git tracing. Git is called a content-addressing file system for good reason.

We can do an experiment. Initialize the local repository twice, each time creating a new markdown file that says ## git is awesome, and write down the full 40-character checksum to see if they are the same.

.git/objects/56/46a656f6331e1b30988472fefd48686a99e10f
Copy the code

If you actually do the experiment, you’ll see that even if the file names and file formats are different, as long as the contents are the same, their checksums are the same, and they are the checksums listed above.

Git should now have a better understanding of what you’re tracking.

The same content refers to an object

Developers are keen to avoid this, but how does Git handle multiple files with the same content in a repository?

We initialize a local repository and create two new files with different names that say ## git is awesome. What happens to the mysterious objects directory after you run git add.?

.git/objects/56/46a656f6331e1b30988472fefd48686a99e10f
Copy the code

There’s only one directory, and the checksum is the same as before.

Git is such an excellent tool. How could it waste disk space? Since multiple files have the same content, you must save only one object and let them refer to it here.

File changes correspond to new objects

We now assume that files in the workspace correspond to objects in the Objects directory. But is this really the case?

Initialize a local repository, create a new markdown file, and run Git add. Command. You now have an object in the Objects directory. Git is awesome ## git is awesome Run the git add. Command again.

.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
.git/objects/56/46a656f6331e1b30988472fefd48686a99e10f
Copy the code

Well, two objects appear in the Objects directory. The first object must correspond to an empty file. The second object, which we are all too familiar with, corresponds to the added content file.

Again, Git is a version management system, where files are not the hero, versions are. We just staged it twice, so you can assume that the staging area now has two versions (the staging area version is actually a copy of the content, not the actual version). Of course you need two objects to hold it.

All file changes are saved

Initialize a local repository and add the uncompressed version of Lodash.js 4.17.11 and approximately 540KB to the workspace. After running git add., an object appears under the objects directory, which is approximately 96KB in size.

.git/objects/cb/139dd81ebee6f6ed5f5a9198471f5cdc876d70
Copy the code

We made a small change to the contents of the lodash.js file, changing the version number from 4.17.11 to 4.17.10 and running git add again. Command. Then you’ll be surprised to see that there are two objects in the Objects directory. The surprise is not that, but that the second object is also about 96KB in size.

.git/objects/cb/139dd81ebee6f6ed5f5a9198471f5cdc876d70
.git/objects/bf/c087eec7e61f106df8f5149091b8790e6f3636
Copy the code

I just changed one number, and the second object is still the same size.

I just said git will be careful and prudent, how come you don’t know the depth of this? This is because when multiple files have the same content, referencing the same object does not reduce query efficiency, and if only increments are stored between objects in the staging area, querying and switching between versions takes extra time, which is not cost-effective.

But keeping it all is not an option. But Git wants to have it both ways, and it does. We’ll talk about that later.

Renaming is split into delete and new actions

Initialize a local repository, create a new file, and run git add. Then rename the file to see the status information.

$ git status

On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   a.md
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    deleted:    a.md
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    b.md
Copy the code

This is due to git’s internal mechanics. When the object is generated, it finds that a file with that name is missing from the repository and marks it as deleted. It also finds a new file name that has not been marked before and marks it as untraced. Because it’s just a rename, the file content doesn’t change, so objects can be shared without affecting efficiency.

A blob object

All the secrets of Git are in the.git directory. Because it has complete information about the project, Git must have the backups stored somewhere. Where and how does Git store them?

Git calls this backup information objects. Git has four types of objects, all of which exist in the git/ Objects directory.

This time we will only cover bloB objects.

It stores the content and size of the file. Blob objects are generated when a developer adds an untraced file or changes to the traced file to the staging area. Git zlib compresses blob objects to reduce space footprint.

Because it stores only content and size, two files can share a BLOB object, even if the file name and format are completely different, as long as the content is the same.

Note that bloB objects and files in the working directory are not one-to-one, because files in the working directory are almost always added to the staging area multiple times, where one file corresponds to multiple BLOB objects.

index

The repository’s.git directory contains a file known as the staging area.

Yes, the staging area is not a region, but a file, or rather, an index file.

It holds references to the project structure, filename, timestamp, and BLOB object.

It is through this index file that files in the workspace and BLOB objects are associated.

packaging

Remember how you tried to have your cake and eat it?

Want to save the full amount, do not reduce the search and switching speed, and want to squeeze the volume as much as possible. How does Git do this?

Git packages Git objects periodically or before pushing them to the remote end.

The latest full version of the file is saved when packaging, and changes based on previous versions of the file are saved only diff information. Because developers rarely switch to an earlier version, efficiency can be partially sacrificed.

Note that all Git objects are packaged, not just blob objects.

Git also has a Git GC command to perform packaging manually.

$ git gc

Counting objects: 11, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (11/11), done.
Total 11 (delta 3), reused 0 (delta 0)
Copy the code

The original Git object files are missing and there are two more files in the pack folder. The.pack suffix stores the actual contents of the git object file before packaging.

.git/objects/
.git/objects/info/
.git/objects/info/packs
.git/objects/pack/
.git/objects/pack/pack-99b4704a207ea3cc4924c9f0febb6ea45d4cdfd2.idx
.git/objects/pack/pack-99b4704a207ea3cc4924c9f0febb6ea45d4cdfd2.pack
Copy the code

Let’s just say git GC isn’t semantically good enough. Its function is not only garbage collection, but also packaging.

02) commit

Git is a version management system. The ultimate goal is to keep information about a project at a particular time in a version that can be returned and reviewed in the future.

The next step in the staging area is the repository, which is facilitated by the Git commit command.

submit

If you run git commit in the staging area, git will jump to the default editor and ask you to enter the commit instructions. You can also customize the editor to jump to.


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Initial commit
# Changes to be committed:
# new file: a.md
Copy the code

This is what we see after submission.

[master (root-commit) 99558b4] commit for nothing
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a.md
Copy the code

What if I just don’t write a submission note?

Aborting commit due to empty commit message.
Copy the code

Git commits to git.

If the submission description is not large, you can add -m to the command to enter the submission description.

$ git commit -m "commit for nothing"
Copy the code

You can even add staging and commit at the same time.

$ git commit -am "commit for nothing"
Copy the code

Note, however, that as with git add -u, untracked files cannot be committed.

rewriting

Amend is translated into Chinese to amend. Git commit — The amend command allows you to modify the last commit.

$ git log --oneline

8274473 (HEAD -> master) commit for nothing
Copy the code

There is currently only one COMMIT in the project commit history. It suddenly occurred to me that there was a clerical error in this submission. I wrote Gao Yuanyuan as Gao Xiaosong (really a clerical error). However, I don’t want to add a commit for this clerical error. After all, it’s just a small clerical error. Most importantly, I want to correct it quietly, so as not to be laughed at by others.

I can use git commit –amend.

First modify Gao Xiaosong into Gao Yuanyuan.

Then run git add a.m. d.

Finally, rewrite the commit. Git will jump to the default or custom editor to prompt you to change the commit instructions. Of course you don’t have to.

$ git commit --amend

commit for nothing
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Date: Thu Jan 3 09:33:56 2019 +0800
# On branch master
# Initial commit
# Changes to be committed:
# new file: a.md
Copy the code

Let’s look at the commit history.

$ git log --oneline

8a71ae1 (HEAD -> master) commit for nothing
Copy the code

There is also only one COMMIT in the commit history. Note, however, that the commit is not the same as the previous commit, and their checksums are different. This is called rewriting.

Tree object and Commit object

The COMMIT operation involves two Git objects.

The first is the tree object.

It stores references to subdirectories and subfiles. If there were only BLOB objects, the repository would be a mess. It is the tree objects that register their relationships that make up a structured version library.

The add to staging operation does not generate a Tree object, so the structure of the project is stored in the index file until the commit repository operation generates a tree object for each directory.

The second is the COMMIT object.

It stores information for each commit, including a reference to the root tree object currently committed, a reference to the parent COMMIT object, author and committer, and commit information. The version actually refers to the commit object.

The author and the submitter are usually the same person, but there can be different people.

objects

Initialize a Git project and create some new files and directories.

src/
src/a.md
lib/
lib/b.md
Copy the code

First, run the git add command. We know that this will generate a BLOb object in.git/objects, because currently both files are empty and share a blob object.

.git/objects/info/
.git/objects/pack/
.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Copy the code

Now let’s run git commit and see what happens.

.git/objects/info/
.git/objects/pack/
.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
.git/objects/93/810bbde0f994d41ef550324a2c1ad5f9278e19
.git/objects/52/0c9f9f61657ca1e65a288ea77d229a27a8171b
.git/objects/0b/785fa11cd93f95b1cab8b9cbab188edc7e04df
.git/objects/49/11ff67189d8d5cc2f94904fdd398fc16410d56
Copy the code

Interesting. There was only one blob object. How can so many Git objects suddenly pop up? Git tree and Git commit. Git tree and Git commit.

Git cat-file -t

git cat-file -t

git cat-file -t


This is the first tree object.

$ git cat-file -t 93810bb

tree
Copy the code
$ git cat-file -p 93810bb

100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    b.md
Copy the code

This is the second tree object.

$ git cat-file -t 520c9f9

tree
Copy the code
$ git cat-file -p 520c9f9

100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    a.md
Copy the code

This is the third tree object.

$ git cat-file -t 0b785fa

tree
Copy the code
$ git cat-file -p 0b785fa

040000 tree 93810bbde0f994d41ef550324a2c1ad5f9278e19    lib
040000 tree 520c9f9f61657ca1e65a288ea77d229a27a8171b    src
Copy the code

As you can see, each directory generates a corresponding tree object at commit time.

Then we look at the COMMIT object.

$ git cat-file -t 4911ff6

commit
Copy the code
$ git cat-file -p 4911ff6

tree 0b785fa11cd93f95b1cab8b9cbab188edc7e04df parent c4731cfab38f036c04de93facf07cae496a124a2 author veedrin <[email protected]> 1546395770  +0800 committer veedrin <[email protected]> 1546395770 +0800 commitfor nothing
Copy the code

As you can see, commit associates the root tree object with all project structure information. It also has to associate a parent commit, its last commit, to form a version history. Of course, there is no parent commit if it is the first commit. Then there are the COMMIT instructions and some participant information.

Git add generates blob objects for content or files added to the staging area, and git commit generates tree objects and commit objects for content or files added to the repository. So far, we’ve seen three of the four Git objects.

Why not generate a tree object with git add?

A staging area is a temporary place for preparation of information that is not necessarily saved as a version. Git thinks that it is not efficient to generate a tree object while git is added. It can be generated when the version is finalized. The structure information before versioning is stored in the index file.

03) branch

Branches are a powerful weapon that makes Git so flexible, and it is because of clever branch design that many Git workflows are possible.

Now we know that the commit object is actually the version in Git. Can we switch between versions only by specifying the meaningless SHA-1 value of the COMMIT object?

Of course not.

In Git, you can do this easily by pointing Pointers to commit objects, called branches.

Branching is an ambiguous concept in Git.

You can think of it as just a pointer to a COMMIT object node.

You can also think of it as a pointer to a commit object node that traces the commit history between some crossover node.

Strictly speaking, one is called a branch pointer and one is called a branch history. In practice, however, they are often not distinguished by name.

So we need to understand the meaning behind the text, whether it’s a branch pointer or a branch history.

Most of the time, it refers to a branch pointer.

The master branch

Git /refs/heads directory is empty. This is because there are currently no Commit objects in the repository, and branches must point to commit objects.

Once you have the first commit in your repository, git automatically generates a master file in the.git/refs/heads directory, which is the default branch of Git. It’s not special, it just acts as a default role.

The newly initialized git repository will show that it is currently on the master branch, which is a fake. Git /refs/heads directory does not contain the file. The default branch will only be true if the commit history is not empty.

So let’s see what the master file actually has.

$ cat .git/refs/heads/master

6b5a94158cc141286ac98f30bb189b8a83d61347
Copy the code

40 characters, obviously a reference to a Git object. Again, identify its type and find that it is a COMMIT object.

$ git cat-file -t 6b5a941

commit
Copy the code

As simple as that, a branch (branch pointer) is a pointer to a COMMIT object.

Pointer to the HEAD

Figuratively speaking, a HEAD is an icon on a scenic map that indicates where you are.

HEAD is where you are right now. It usually points to some branch, because we’re always above some branch.

Since HEAD is used to mark the current location, once the HEAD position is changed, the working directory is switched to the branch that HEAD points to.

$ git log --oneline

f53aaa7 (HEAD -> master) commit for nothing
Copy the code

There are exceptions, such as when I check out directly to a COMMIT that has no branch reference.

$ git log --oneline

cb64064 (HEAD -> master) commit for nothing again
324a3c0 commit for nothing
Copy the code
$ git checkout 324a3c0

Note: checking out '324a3c0'.
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.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
  git checkout -b <new-branch-name>
HEAD is now at 324a3c0... commit for nothing
Copy the code
$ git log --oneline

324a3c0 commit for nothing
Copy the code

A HEAD in this case is called a detached HEAD.

Keep in mind that only a commit between the initial commit and a branch is valid. As your HEAD is in the detached HEAD state, the new commit on top of it is not wrapped in any branches. Once you switch to another branch, the commit is (possibly) never referenced again and will eventually be removed by the garbage collection mechanism. So it’s a very dangerous operation.

324a3c0 -- cb64064(master)
   \
 3899a24(HEAD)
Copy the code

If you accidentally do this, either create a new branch in place, or forcibly move an existing branch over. Make sure it’s not forgotten.

Death is not the end, forgetting is. — Coco

create

In addition to the default master branch, we can create new branches at will.

$ git branch dev
Copy the code

A dev branch is created.

To view

Sometimes you might want to check how many branches there are in your local repository, because it’s so easy to create new branches in Git.

$ git branch

  dev
* master
Copy the code

The current branch is preceded by an asterisk.

To view both local and remote branch references, add the -a parameter.

$ git branch -a

* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
Copy the code

delete

When a branch is no longer needed after it is merged, it should be removed.

$ git branch -d dev

Deleted branch dev (was 657142d).
Copy the code

Sometimes we get a different cue.

$ git branch -d dev

error: The branch 'dev' is not fully merged.
If you are sure you want to delete it, run 'git branch -D dev'.
Copy the code

This is a safeguard for Git. Next step is not fully merged into the current branch. Do you want to delete it?

Most of the time, of course.

$ git branch -D dev

Deleted branch dev (was 657142d).
Copy the code

-d is short for –delete –force. You can also write -df.

Note that deleting a branch simply deletes a pointer and does not delete the corresponding COMMIT object. It is possible, however, that once the branch is deleted, the string of Commit objects is no longer referenced and thus deleted by the garbage collection mechanism.

04) checkout

In Git, there are several backups in the staging area and several versions in the repository. There’s got to be some use for this stuff, right? How? I switch to one when I need one.

This is what the git checkout command, officially called checkout, does.

How do you understand checkout? Checkout originally meant when you make a purchase, the clerk checks your bill with you, and then you’re ready to go. In Git, checking refers to diff, comparing the differences between two versions, and switching over if no conflicts are found.

The underlying

We know that the HEAD pointer points to the current version and git checkout is used to switch versions, so they must be related.

Currently the HEAD pointer points to the master branch.

$ cat .git/HEAD

ref: refs/heads/master
Copy the code

What happens if I switch to another branch?

$ git checkout dev

Switched to branch 'dev'
Copy the code
$ cat .git/HEAD

ref: refs/heads/dev
Copy the code

Sure enough, the git checkout command works by changing the HEAD pointer. If the HEAD pointer changes, Git will extract the version of the HEAD pointer as the version of the current working directory. The same goes for checking out a COMMIT that has no branch references.

symbol

Before we get down to business, let’s talk about the two symbols ~ and ^ in Git.

If we want to switch from one branch to another, that’s fine, semantic enough. But if we want to switch to a COMMIT, how else can we quickly reference it, other than meticulously finding its SHA-1 value?

For example, we can quickly locate based on pedigree relationships between commits.

$ git log --graph --oneline

* 4e76510 (HEAD -> master) c4
*   2ec8374 c3
|\  
| * 7c0a8e3 c2
* | fb60f51 c1
|/  
* dc96a29 c0
Copy the code

The function of ~ is longitudinal orientation. It goes all the way back to the earliest ancestor, Commit. If the commit history forks, it picks the first one, the one on the trunk.

The function of ^ is to position in the landscape. It cannot trace up, but if the commit history has forks, it can locate any one of them.

HEAD does not add any symbol, plus~ 0Sign or addition^ 0Symbol is used to locate the current version

Needless to say, locate the current COMMIT.

$ git rev-parse HEAD

4e76510fe8bb3c69de12068ab354ef37bba6da9d
Copy the code

It means to locate the zeroth parent COMMIT, which is the current COMMIT.

$ git rev-parse HEAD~0

4e76510fe8bb3c69de12068ab354ef37bba6da9d
Copy the code

It represents the 0th parent commit that locates the current COMMIT, which is the current commit.

$ git rev-parse HEAD^0

4e76510fe8bb3c69de12068ab354ef37bba6da9d
Copy the code

with~The number of symbols piled up orThe number ~Write to locate the generation of parent commit

$ git rev-parse HEAD~~

fb60f519a59e9ceeef039f7efd2a8439aa7efd4b
Copy the code
$ git rev-parse HEAD~2

fb60f519a59e9ceeef039f7efd2a8439aa7efd4b
Copy the code

withThe number ^Locate the number of parent commit

Note that ^ locates the parent commit of the current base.

$ git rev-parse HEAD^

2ec837440051af433677f786e502d1f6cdeb0a4a
Copy the code
$ git rev-parse HEAD^1

2ec837440051af433677f786e502d1f6cdeb0a4a
Copy the code

Because the current commit has only one parent commit, locating the second parent will fail.

$ git rev-parse HEAD^2

HEAD^2
fatal: ambiguous argument 'HEAD^2': unknown revision or path not in the working tree.
Use The '-' to separate paths from revisions, like this:
'git  [
      
       ...]  -- [
       
        ...] '
       
      
Copy the code

with~ Quantity ^ quantityIs written or^ Number ^ numberFind the parent commit of the generation of the parent commit

The parent of the current commit is the 0th parent of a commit.

$ git rev-parse HEAD~^0

2ec837440051af433677f786e502d1f6cdeb0a4a
Copy the code

For example, the first parent of the current commit is located here. Again, note that ^ locates the parent commit of the current base.

$ git rev-parse HEAD~^1

fb60f519a59e9ceeef039f7efd2a8439aa7efd4b
Copy the code

This is the second parent of the first-generation commit of the current commit.

$ git rev-parse HEAD~^2

7c0a8e3a325ce1b5a1cdeb8c89bef1ecf17c10c9
Copy the code

Similarly, locating a commit that does not exist will fail.

$ git rev-parse HEAD~^3

HEAD~^3
fatal: ambiguous argument 'HEAD~^3': unknown revision or path not in the working tree.
Use The '-' to separate paths from revisions, like this:
'git  [
      
       ...]  -- [
       
        ...] '
       
      
Copy the code

Unlike ~, ^2 and ^^ have different effects. ^2 refers to the second parent commit, ^^ refers to the first parent commit of the first parent commit.

Switch to the HEAD

Git checkout takes the HEAD argument by default if it does not take any arguments. The HEAD pointer points to the current COMMIT. So it doesn’t do any checkout.

What was not mentioned earlier is that the Git checkout command has the side-effect of comparing the checked out version with the staging area.

So git checkout takes no arguments, which means comparing the current commit to the staging area.

$ git checkout

A   b.md
Copy the code
$ git checkout HEAD

A   b.md
Copy the code

Switch to commit

Switching branches is of course what developers use most. You can follow checkout with both the branch name and the checksum of the commit. You can also use symbols to locate the commit.

$ git checkout dev

Switched to branch 'dev'
Copy the code
$ git checkout acb71fe

Note: checking out 'acb71fe11f78d230b860692ea6648906153f3d27'.
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.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
  git checkout -b <new-branch-name>
HEAD is now at acb71fe... null
Copy the code
$ git checkout HEAD~2

Note: checking out 'acb71fe11f78d230b860692ea6648906153f3d27'.
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.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
  git checkout -b <new-branch-name>
HEAD is now at acb71fe... null
Copy the code

Create a branch and switch

Sometimes when you create a branch you want to switch to the created branch at the same time. Just git branch

cannot do this. The Git checkout command provides a quick way to create and switch branches in one step.

$ git checkout -b dev

Switched to a new branch 'dev'
Copy the code

The staging area file overwrites the workspace file

Git Checkout not only performs a full switch like commit, it also performs a micro switch on a file basis.

$ git status

On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   a.md
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   a.md
Copy the code
$ git checkout -- a.md
Copy the code
$ git status

On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   a.md
Copy the code

Since the staging area overwrites the workspace, the workspace changes are undone, and now only the staging changes are left to commit. In fact, it is equivalent to undoing the changes of the file in the workspace, but its semantics are overwrite. This command does not prompt you to undo workspace changes. Use it with caution.

Git checkout —

Git checkout —

. Git checkout —

Git checkout

But don’t worry, I mean the two commands are trying to do the same thing, but the actual effect is slightly different.

Independent — arguments on the Linux command line are: treat the following arguments as file names. When followed by a file name, it is best to add a separate — parameter to avoid ambiguity.

That is, if the project happens to have a branch named a.d, the independent — parameter will not operate on the branch, but on the file.

If you don’t feel good about just undoing a file and making changes to your workspace, you’re not taking it personally, you’re just saying workspace changes are crap. Then there is an even more dangerous order.

$ git checkout -- .
Copy the code

. Represents all files and subdirectories in the current directory. This command will undo all workspace changes.

The current COMMIT file overwrites the staging and workspace files

If you execute git checkout —

with a checksum of the branch name or commit, the effect is that the current version of the file overwrites both the staging area and workspace. This is equivalent to undoing changes to the file in the staging area and the workspace at the same time.

$ git status

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
    modified:   a.md
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   a.md
Copy the code
$ git checkout HEAD -- a.md
Copy the code
$ git status

On branch master
nothing to commit, working tree clean
Copy the code

Git checkout is a git checkout command that overwrites a file even if it conflicts with what you’re overwriting.

05) merge

The ease of creating branches is one of the main reasons git is so popular, and using Git Checkout

also allows developers to move freely between branches. However, when the river finally reaches the sea, the work done on the other branches will eventually be merged into the main branch.

So let’s look at merge operations in Git.

Git merge Requires some preparation before executing git merge.

$ git merge dev

error: Your local changes to the following files would be overwritten by merge:
    a.md
Please commit your changes or stash them before you merge.
Aborting
Copy the code

Before you merge, you must ensure that there is nothing to commit in the staging area; otherwise git will block the merge. This is because git overwrites the staging area after the merge. So there’s a risk of losing your work.

Git won’t stop you from adding workspace content to the staging area. Maybe Git doesn’t think it’s important.

However, it is best to keep a clean workspace before performing the merge.

Merging of different branches

Different branches refer to the fact that the two Commits to be merged diverge after an ancestor commit.

C0 -- C1 -- C2(HEAD -> master)
       \
        C3(dev)
Copy the code

Git merge followed by merge object means to merge it in.

$ git merge dev
Copy the code

At this point, if there are no conflicts, Git will pop up a default or custom editor that lets you fill in the commit instructions. Of course it will give you a default commit description.

Merge branch 'dev'

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
Copy the code

Why do YOU need to fill out the COMMIT instructions? Git merge actually creates a new commit object, records the merge information, and moves the current branch pointer to it.

C0 -- C1 -- C2 -- C4(HEAD -> master)(merge commit)
       \          /
        \        /
          C3(dev)
Copy the code

Git merge merge git merge git merge git merge Git merge Git merge git merge Git merge Git merge

What does a tripartite union mean?

Git will extract the diff of the merge subject commit relative to the common ancestor of the merge subject and object, and compare the diff of the merge object commit relative to the common ancestor of the merge subject and object, and then compare whether the two diff have changed the same place, where the unit of the same place is the line of the file. If not, merge the two diff copies to produce a new COMMIT, with the current branch pointer moving to the right. If so, it’s up to the developers to fix it themselves.

So in a three-way merge, the commit, the common ancestor of the merging subject and object, is just a reference.

The merging subject is upstream from the merging object

It refers to the fact that the developer is currently on a COMMIT node and is merging the updated Commit nodes on the same branch.

C0 -- C1 -- C2(HEAD -> master) -- C3(dev)
Copy the code

What happens at this point?

This is equivalent to updating the current branch pointer, so simply move the current branch pointer downstream so that the merge subject and merge object point to the same COMMIT. A new COMMIT is not created.

Using the concept of three-way merge, the merge subject commit and the common ancestor of the merge subject and object commit are the same commit, and the diff of the merge subject commit is empty relative to the common ancestor of the merge subject and object commit. The merge object COMMIT’s diff merges itself with the empty diff relative to the merge subject’s common ancestor, the COMMIT, so it does not need to generate a new COMMIT.

$ git merge dev Updating 9242078.. 631ef3a Fast-forward a.md | 2 ++ 1 file changed, 2 insertions(+)Copy the code
C0 -- C1 -- C2 -- C3(HEAD -> master, dev)
Copy the code

Git has a name for this operation: Fast Forward.

This often happens with Git pull, for example. Git pull is usually executed because there is an update commit on the remote end. In this case, the remote end is the merge object, the local end is the merge subject, and the branch pointer on the remote end is downstream, which also triggers the Fast Forward.

The merging subject is downstream from the merging object

If the merge subject is downstream of the merge object, the merge subject itself contains the merge object, and the merge operation has no effect.

C0 -- C1 -- C2(dev) -- C3(HEAD -> master)
Copy the code
$ git merge dev

Already up to date.
Copy the code
C0 -- C1 -- C2(dev) -- C3(HEAD -> master)
Copy the code

In this case, the merge object COMMIT and the common ancestor of the merge subject and object commit are the same COMMIT, and the diff of the merge object COMMIT is empty relative to the common ancestor of the merge subject and object commit. The merge principal commit’s diff merges with the empty diff itself relative to the common ancestor of the merge subject and object. But this time it doesn’t have to move, because the merged diFF is its own diFF.

Notice, does the dev branch pointer move?

Git merge has no effect on merge objects.

Merge multiple objects at the same time

If you merge git with more than one branch, it means you want to merge them into the current branch at the same time.

$ git merge aaa bbb ccc

Fast-forwarding to: aaa
Trying simple merge with bbb
Trying simple merge with ccc
Merge made by the 'octopus' strategy.
 aaa.md | 0
 bbb.md | 0
 ccc.md | 0
 3 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 aaa.md
 create mode 100644 bbb.md
 create mode 100644 ccc.md
Copy the code

Git merges have several strategies, the ‘Octopus’ Strategy used above, because multiple branches merged at the same time all end up pointing to a new COMMIT, looking like tentacles of an octopus.

Merge conflict

Git merge operations are not always so smooth. Because sometimes the branches to be merged are not owned by the same person, there is a high probability that both people will modify a line of the file at the same time. Git doesn’t know whose version to use, and it thinks the two branches are in conflict.

At this point, it is up to the developer to manually resolve the conflict to allow Git to continue merging.

$ git merge dev

Auto-merging a.md
CONFLICT (content): Merge conflict in a.md
Automatic merge failed; fix conflicts and then commit the result.
Copy the code

Let’s take a look at what a conflicting file looks like.

<<<<<<< HEAD
apple
=======
banana
>>>>>>> dev
Copy the code

Run git status.

$ git status

On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)
Unmerged paths:
  (use "git add <file>..." to mark resolution)
    both modified:   a.md
no changes added to commit (use "git add" and/or "git commit -a")
Copy the code

Once the conflict is resolved, you need to commit again to tell Git it’s ready to merge.

$ git commit -m "fix merge conflict"

U   a.md
error: Committing is not possible because you have unmerged files.
hint: Fix them up in the work tree, and then use 'git add/rm <file>'
hint: as appropriate to mark resolution and make a commit.
fatal: Exiting because of an unresolved conflict.
Copy the code

Oh, no. Are you remembering your own love story?

By the time we resolve the conflict, the workspace has changed, so we need to commit to the staging area first.

$ git add a.md
Copy the code
$ git commit -m "fix merge conflict"

[master 9b32d4d] fix merge conflict
Copy the code

You can also use git merge –continue to replace git commit after running git add. It makes the subsequent behavior behave the same as if there were no conflicts.

If you have a conflict and you don’t know how to resolve it, you have to ask your partner why you changed it. You want to go back to where you were before the merger.

This is easy for Git. Simply run git merge –abort.

$ git merge --abort
Copy the code

This command does not guarantee the restoration of workspace changes, so it is best to keep the workspace clean before merging.

06) rebase

Git merge generates a new merge commit. If you’re obsess and don’t like the new merge commit, Git has a more refreshing solution for you: the git rebase command.

Git is Doraemon’s pocket.

Rebase translates to change base. This means recommitting all the commitments to be merged on the new basis.

Basic usage

Git rebase

calculates the most recent common ancestor of the current and target branches, and then bases all commits between the most recent common ancestor and the current branch to the target branch, making the commit history a straight line.

C0 -- C1 -- C2 -- C3(master)
       \
        C4 -- C5 -- C6(HEAD -> dev)
Copy the code

Merge is different from the branch name followed by rebase. Merge is merge in, change base is change base past, you get a feel for it.

$ git rebase master

First, rewinding head to replay your work on top of it...
Applying: C4.md
Applying: C5.md
Applying: C6.md
Copy the code
C0 -- C1 -- C2 -- C3(master) -- C4' -- C5' -- C6'(HEAD -> dev) \ C4 -- C5 -- C6Copy the code

All commits between the most recent common ancestor and the current branch are now copied after the master branch and the HEAD pointer is switched over to the current branch pointer. Well, that’s a pretty neat trick, because you can’t tell the difference if you’re in it.

Is the original COMMIT still there? As a detached HEAD, Git will remind you that you are in the detached HEAD state if you remember its commit checksum. They just don’t have any branch Pointers to them, they’ve been discarded, and the rest of the time is spent waiting for git garbage collection to clean them up.

Good thing, some people remember them, don’t they?

Git rebase is not complete because the target branch is master and the current branch is dev. I need to switch to the Master branch and merge again.

$ git checkout master
Copy the code
$ git merge dev
Copy the code

Oh, come on, is there a merger?

Don’t worry, this merge is Fast forward and does not generate a new merge COMMIT.

What if I want to base the ontology branch on something other than the current branch? That’s ok.

$ git rebase master dev
Copy the code

If you’re on any branch, you can base the dev branch on the master branch, and when you base the current branch, it becomes dev.

Clipping commit base change

Rebasing is a bit like gene editing, git has more precise tools to get what you want.

Thanks to precise gene editing technology, mom won’t have to worry about your ugliness anymore.

C0 -- C1 -- C2 -- C3(master)
       \
        C4 -- C5 -- C6(dev)
         \
          C7 -- C8(HEAD -> hotfix)
Copy the code
$ git rebase --onto master dev hotfix

First, rewinding head to replay your work on top of it...
Applying: C7.md
Applying: C8.md
Copy the code
C0 -- C1 -- C2 -- C3(master) -- C7' -- C8'(HEAD -> hotfix)
       \
        C4 -- C5 -- C6(dev)
         \
          C7 -- C8
Copy the code

The onto parameter is the gene-editing scissors.

It clipped the hotfix branch to the commit between the hotfix branch and the most recent common ancestor of the Dev branch and copied it to the target base point. Note that this refers to ranges that do not include the most recent common ancestor commit, as C4commit will not be copied here.

$ git rebase --onto master dev

First, rewinding head to replay your work on top of it...
Applying: C7.md
Applying: C8.md
Copy the code

If — write only two branch (or commit) names after onto, the third branch (or commit) defaults to the branch (or commit) to which the HEAD pointer points.

Change base conflict resolution

There can be conflicts, and let’s see how those conflicts are resolved.

C0 -- C1 -- C2(HEAD -> master)
       \
        C3 -- C4(dev)
Copy the code
$ git rebase master dev

First, rewinding head to replay your work on top of it...
Applying: c.md
Applying: a.md add banana
Using index info to reconstruct a base tree...
M   a.md
Falling back to patching base and 3-way merge...
Auto-merging a.md
CONFLICT (content): Merge conflict in a.md
error: Failed to merge in the changes.
Patch failed at 0002 a.md dev
The copy of the patch that failed is found in: .git/rebase-apply/patch
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>".then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Copy the code

C2 and C4 simultaneously modify a line of A.d., causing a conflict. Git has already given us a hint, basically the same operation as merge.

We can resolve the conflict manually, and then execute git add and git rebase –continue to accomplish the base change.

If you don’t want to overwrite the target commit, you can also skip the commit and do git rebase –skip. Note, however, that this skips the entire commit with the conflict, not just the conflicting parts.

Git rebase –abort

cherry-pick

Git rebase — The onto command can crop branches to base on another branch. It still picks consecutive commit segments, but allows you to specify the start and end.

The git cherry-pick command is a standalone git command, but its effect is still a commit level.

Git cherry-pick can select any commit to base on the target commit. You pick. He picks the base.

usage

Simply follow the git cherry-pick command with the commit checksum to apply it to the target commit.

C0 -- C1 -- C2(HEAD -> master)
       \
        C3 -- C4 -- C5(dev)
               \
                C6 -- C7(hotfix)
Copy the code

Switch the current branch to master branch.

$ git cherry-pick C6

[master dc342e0] c6
 Date: Mon Dec 24 09:13:57 2018 +0800
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 c6.md
Copy the code
C0 -- C1 -- C2 -- C6'(HEAD -> master) \ C3 -- C4 -- C5(dev) \ C6 -- C7(hotfix)Copy the code

The C6commit is recommitted to the master branch as is. Cherry -pick does not modify the original commit.

It’s also convenient to pick multiple commits at the same time, simply stacking them on top of each other.

$ git cherry-pick C4 C7

[master ab1e7c7] c4
 Date: Mon Dec 24 09:12:58 2018 +0800
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 c4.md
[master 161d993] c7
 Date: Mon Dec 24 09:14:12 2018 +0800
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 c7.md
Copy the code
C0 -- C1 -- C2 -- C4' -- C7'(HEAD -> master)
       \
        C3 -- C4 -- C5(dev)
               \
                C6 -- C7(hotfix)
Copy the code

What if the multiple Commits happen to be consecutive?

$ git cherry-pick C3... C7 [master d16c42e] c4 Date: Mon Dec 24 09:12:58 2018 +0800 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 c4.md [master d16c42e] c6 Date: Mon Dec 24 09:13:57 2018 +0800 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 c6.md [master a4d5976] c7 Date: Mon Dec 24 09:14:12 2018 +0800 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 c7.mdCopy the code
C0 -- C1 -- C2 -- C4' -- C6' -- C7'(HEAD -> master) \ C3 -- C4 -- C5(dev) \ C6 -- C7(hotfix)Copy the code

Git starts with something, and usually doesn’t include something.

Git cherry-pick and Git rebase are very similar to each other. Git cherry-pick! Ll: Git cherry-pick!

conflict

Git commands resolve conflicts in much the same way.

C0 -- C1(HEAD -> master)
 \
  C2(dev)
Copy the code
$ git cherry-pick C2

error: could not apply 051c24c... banana
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
Copy the code

To resolve the conflict manually, run the git add command and then run the git cherry-pick –continue command.

If you are bluffed and want to restore, git cherry-pick –abort.

Change base or merge

This is a philosophical question.

There is a view that the commit history of the repository should record what actually happened. So if you merge one branch into another, there should be a trace of that merge in the commit history because it actually happened.

Another view is that the commit history of the repository should record what happened during the project. Merge is not a result of the project development itself; it is an additional operation that makes the commit history long.

I’m a minimalist, so I’m all for going gay first.

07) reset

The Git checkout command, which switches between versions at will, essentially moves the HEAD pointer.

Is there a way to move branch Pointers around in Git?

Git reset.

The underlying

The git reset command differs from the git checkout command in that it moves the HEAD pointer and the branch pointer together if the HEAD pointer points to a branch pointer.

The detached HEAD pointer is referred to as a detached HEAD when you use the git checkout command to switch from a commit with a branch to a commit with no branch. This is very dangerous.

C0 -- C1 -- C2(HEAD -> master)
Copy the code
$ git checkout C1
Copy the code
C0 -- C1(HEAD) -- C2(master)
Copy the code

The git reset command does not have this problem because it carries the current branch pointer with it.

C0 -- C1 -- C2(HEAD -> master)
Copy the code
$ git reset C1
Copy the code
C0 -- C1(HEAD -> master) -- C2
Copy the code

That’s what reset means. It can reset branches.

Let’s do the other case. If you switch from a commit with no branch pointing to another commit with no branch pointing to it, then they are two Korean girls who are confused.

This is the effect of git checkout.

C0 -- C1 -- C2(HEAD) -- C3(master)
Copy the code
$ git checkout C1
Copy the code
C0 -- C1(HEAD) -- C2 -- C3(master)
Copy the code

This is the effect of the git reset command.

C0 -- C1 -- C2(HEAD) -- C3(master)
Copy the code
$ git reset C1
Copy the code
C0 -- C1(HEAD) -- C2 -- C3(master)
Copy the code

Reset both the staging area and the workspace changes

When you add –hard to git reset, the contents of both the staging area and workspace are reset to the commit content. This means that changes in the staging area and workspace are cleared, which is equivalent to undoing changes in the staging area and workspace.

And there is no confirmation operation.

$ git status

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
    modified:   a.md
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   a.md
Copy the code
$ git reset --hard HEAD^

HEAD is now at 58b0040 commit for nothing
Copy the code
$ git status

On branch master
nothing to commit, working tree clean
Copy the code

Reset changes to the staging area only

Git reset with –mixed, or no –mixed, because –mixed is the default, the contents of the staging area will be reset to the new commit content. The workspace changes will not be cleared.

There is also no confirmation operation yo.

$ git status

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
    modified:   a.md
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   a.md
Copy the code
$ git reset HEAD^

Unstaged changes after reset:
M   a.md
Copy the code
$ git status

On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   a.md
no changes added to commit (use "git add" and/or "git commit -a")
Copy the code

For fun, what if the git reset command adds nothing?

Git reset –mixed HEAD git reset –mixed HEAD git reset –mixed HEAD

So what does that mean?

This means that there is no change from the current COMMIT reset to the current commit, right? However — the mixed parameter will undo the changes in the staging area, that’s what it does.

Both the staging area and the workspace changes remain

If you add a –soft parameter to git reset command, you will see that you are gentle. Simply reset the COMMIT, and the changes to the staging area and workspace remain.

More gently, the diff of pre-reset and post-reset commit content is also put into staging.

$ git status

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
    modified:   a.md
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   a.md
Copy the code
$ git diff --staged

diff --git a/a.md b/a.md
index 4a77268..fde8dcd 100644
--- a/a.md
+++ b/a.md
@@ -1,2 +1,3 @@
 apple
 banana
+cherry
Copy the code
$ git reset --soft HEAD^
Copy the code
$ git status

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
    modified:   a.md
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   a.md
Copy the code
$ git diff --staged diff --git a/a.md b/a.md index 4a77268.. Fde8dcd 100644 -- A /a.md +++ B/A.m d @@ -1 +1,3 @@ apple +banana +cherryCopy the code

Banana is the diff between the pre-reset commit and the post-reset commit, and as you can see, it’s already in the staging area.

File staging area contents back to workspace

The git reset command can also be followed by a file name. It resets the contents of the staging area to the contents of the workspace, which is the reverse of git add —

.

Git reset —

is short for git reset HEAD –mixed —

. When manipulating files, only the default –mixed parameter is used.

It does not undo changes made to the workspace.

$ git status

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
    modified:   a.md
Copy the code
$ git reset -- a.md

Unstaged changes after reset:
M   a.md
Copy the code
$ git status

On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   a.md
no changes added to commit (use "git add" and/or "git commit -a")
Copy the code

Git checkout can also be followed by a file name. Git checkout is used to undo workspace changes.

Several commit versions of the file are withdrawn from the workspace

If the Git reset command is followed by a commit checksum, it resets the diff of that commit and all subsequent commits to the workspace.

This means resetting the file back to the commit version you specified, but any changes made after the commit you specified will also be saved in the workspace.

$ git diff --staged

# empty
Copy the code
git reset HEAD~4 -- a.md

Unstaged changes after reset:
M   a.md
Copy the code
$ git diff --staged diff --git a/a.md b/a.md index 6f195b4.. 72943A1 100644 -- A/A.m D +++ B/A.M D @@-1,5 +1 @@ AAA-bbb-CCC-DDD-EeeCopy the code

Git diff — Staged commands compare workspace and staging contents. You can see that the initial workspace and staging area are the same. Resetting the file to 4 versions before the staging area, you can see that there are many more changes in the workspace than in staging area. These are all resets to the workspace after the commit is specified.

08) revert

Sometimes we want to revoke a commit, but the commit is already on a public branch. If you change the branch history directly, you can cause some unnecessary confusion. This is where the Git revert command comes in handy.

Revert translates to restore. I think it makes more sense to call it hedging. Git revert is the term used to refer to two simultaneous transactions that are related to each other, in opposite directions, in the same number of transactions, and that offset each other’s profits and losses.

Because what it does is generate a new, completely opposite COMMIT.

The command

Git revert with the commit you want to hedge.

$ git revert HEAD

Revert "add c.md"
This reverts commit 8a23dad059b60ba847a621b6058fb32fa531b20a.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
# deleted: c.md
Copy the code

Git will pop up a default or custom editor that asks you to enter commit information. A new COMMIT is then generated.

[master a8c4205] Revert "add c.md"
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 c.md
Copy the code

As you can see, ORIGINALLY I added a file a.md. the revert operation will delete it. In the working directory it looks like the add file operation has been undone, but it is actually hedged.

It does not change the commit history, just add a new hedge commit. That’s the best thing about it.

conflict

There’s a conflict going the other way, right? You got to be kidding me.

If you are working on the latest COMMIT, then of course there is no conflict.

What about the previous COMMIT?

C0 -- C1 -- C2(HEAD -> master)
Copy the code

For example, a.d is empty in C0, C1 changes the content of the file to apple, and C2 changes the content of the file to banana. At this point you want to undo the C1 changes.

$ git revert HEAD~

error: could not revert 483b537... apple
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
Copy the code

Let’s take a look at the document.

<<<<<<< HEAD
banana
=======
>>>>>>> parent of 483b537... apple
Copy the code

To resolve the conflict manually, run the git add command and then the git revert –continue command to complete the hedging.

Undelete the revert operation simply by calling git Revert –abort.

09) stash

You are halfway through your work on a branch when you suddenly have an urgent task to attend to. You need to switch to a new branch, but you don’t want to commit the work you’re working on right now.

This scenario requires git’s storage capabilities.

storage

To store the work at hand, simply run the git Stash command.

$ git status

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
    modified:   a.md
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   b.md
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    c.md
Copy the code
$ git stash

Saved working directory and index state WIP on master: 974a2f2 update
Copy the code

WIP stands for work in Progress. It’s work in progress.

$ git status

On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    c.md
nothing added to commit but untracked files present (use "git add" to track)
Copy the code

As you can see, the contents of the workspace and staging area are stored, except for files that are not tracked by Git. Now you can switch to another branch for the next step.

To view

Let’s take a look at the storage list.

$ git stash list

stash@{0}: WIP on master: 974a2f2 apple
stash@{1}: WIP on master: c27b351 banana
Copy the code

restore

When we finish our other work, we’ll definitely come back here and continue the interruption.

$ git stash apply

On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   a.md
    modified:   b.md
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    c.md
no changes added to commit (use "git add" and/or "git commit -a")
Copy the code

Oh, wait. Why are A.M.D changes in the workspace? Yes, the Git Stash default restores all the staging and workspace storage to the workspace. What if I just want to be the same?

$ git stash apply --index

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
    modified:   a.md
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   b.md
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    c.md
Copy the code

Adding a parameter –index will make the workspace go to the workspace and the staging go to the staging.

It is also important to note that the restore store operation can be applied to any branch, and it does not care whether the workspace and staging areas are clean on the branch where the store is to be restored. If there is a conflict, resolve it.

We’ve gone through the stash list and shown that the Git Stash apply simply restores the most recent stash.

$ git stash apply stash@{1}
Copy the code

By specifying the storage name, we can restore any storage in the list.

At this point, let’s look at the storage list.

$ git stash list

stash@{0}: WIP on master: 974a2f2 apple
stash@{1}: WIP on master: c27b351 banana
Copy the code

Ah, there are still two. Haven’t I already recovered one?

The word apply is clever, it just applies, it doesn’t clean up.

Clean up the

To clean up the stash list, run the git Stash drop command explicitly.

$ git stash drop stash@{1}
Copy the code
$ git stash list

stash@{0}: WIP on master: 974a2f2 apple
Copy the code

Now there really isn’t. Hope you haven’t been drinking 🙃.

Git also provides a quick way to restore and clean your stash at the same time by running the git stash pop command.

$ git stash pop
Copy the code

10) view

There are four git commands that you can use to view git repository information.

status

The git status command displays both workspace and staging diff, staging and current version diff, and files that are not tracked by Git.

$ git status

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
    modified:   a.md
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   b.md
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    c.md
Copy the code

This command is probably one of the most commonly used Git commands and should be reviewed before committing.

Git status -v is equivalent to git status and Git diff –staged.

$ git status -v

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
    modified:   a.md
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   b.md
Untracked files:
  (use "git add <file>..." to include inwhat will be committed) c.md diff --git a/a.md b/a.md index 5646a65.. 4c479de 100644 --- a/a.md +++ b/a.md @@ -1 +1 @@ -apple +bananaCopy the code

The git status -vv command is the sum of the git status command and git diff command.

$ git status -vv

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
    modified:   a.md
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
    modified:   b.md
Untracked files:
  (use "git add <file>..." to include inwhat will be committed) c.md Changes to be committed: diff --git c/a.md i/a.md index 5646a65.. 4c479de 100644 --- c/a.md +++ i/a.md @@ -1 +1 @@ -apple +banana -------------------------------------------------- Changes not stagedforcommit: diff --git i/b.md w/b.md index e69de29.. 100644-637 a09b I/b.m d + + + w/b.m d @ @ - 0, 0, + 1 @ @ +## git is awesome
Copy the code

There is also a -s argument, which gives us an interesting result.

$ git status -s

M  a.md
 M b.md
?? c.md
Copy the code

Notice that the positions of the letters in front are different.

The first location is the state of the file in the staging area, and the second location is the state of the file in the workspace. For example, the following information shows that the A.M.D file has changes to commit in the staging area and changes to be staged in the workspace.

MM a.md
Copy the code

There are several types of abbreviated status codes:

Status code meaning
M The content of the file has been modified
A File added
D File deleted
R The file has been renamed
C File copied
U File conflict unresolved
? Files are not tracked by Git
! Files are ignored by Git

? And! The state represented is the same at all times because it is not in the Git version system. Like?? Or!!!!! Like this.

show

Git show: git show Git object.

$ git show commit 2bd3c9d7de54cec10f0896db9af04c90a41a8160 Author: veedrin <[email protected]> Date: Fri Dec 28 11:23:27 2018 +0800 update diff --git a/README.md b/README.md index e8ab145.. 75625ce 100644 -- a/ readme.md +++ b/ readme.md @@ -5,3 +5,5 @@ one two three ++ fourCopy the code

Git show is equivalent to git show HEAD, which displays information about the commit object to which the current HEAD points.

Of course, you can also view information about a Git object, followed by the checksum of the Git object.

$ git show 38728d8

tree 38728d8
README.md
Copy the code

diff

The git diff command shows the difference between two principals.

Difference between workspace and staging area

Git diff alone shows the difference between workspace and staging.

$ git diff diff --git a/a.md b/a.md index e69de29.. 100644 - a / 5646 a65 arjun d + + + b/arjun d @ @ - 0, 0, + 1 @ @ +## git is awesome
Copy the code

Because it is a comparison between two principals, Git always names the two principals A and B.

You can also view only the diff of a file. Of course, this is still the difference between workspace and staging area.

$ git diff a.md
Copy the code

Difference between the staging area and the current COMMIT

Git diff — Staged commands show the difference between staging areas and the current COMMIT.

Git diff –cached can do the same thing; it is older and less semantic than –staged.

$ git diff --staged diff --git a/b.md b/b.md index e69de29.. 100644 - a / 4 c479de b.m d + + + b/b.m d @ @ - 0, 0, + 1 @ @ + appleCopy the code

Also, show the difference between a file staging area and the current COMMIT.

$ git diff --staged a.md
Copy the code

The difference between two Commits

Git diff can also be used to see the difference between two commits.

$ git diff C1 C2 diff --git a/a.md b/a.md index e69de29.. 100644 - a / 5646 a65 arjun d + + + b/arjun d @ @ - 0, 0, + 1 @ @ +## git is awesomediff --git a/b.md b/b.md new file mode 100644 index 0000000.. e69de29Copy the code

It’s important to note the order, if I change the order.

$ git diff C2 C1 diff --git a/a.md b/a.md index 5646a65.. 100644 - a/e69de29 arjun d + + + + b/arjun d @ @ - 1, 0, 0 @ @ -## git is awesomediff --git a/b.md b/b.md deleted file mode 100644 index e69de29.. 0000000Copy the code

Compare a file’s differences between two Commits.

$ git diff C1:a.md C2:a.md diff --git a/a.md b/a.md index e69de29.. 100644 - a / 5646 a65 arjun d + + + b/arjun d @ @ - 0, 0, + 1 @ @ +## git is awesome
Copy the code

log

The git log command displays the commit history.

$ git log

commit 7e2514419ec0f75d1557d3d8165a7e7969f08349
Author: veedrin <[email protected]>
Date:   Sat Dec 29 11:56:53 2018 +0800
    c.md
commit 4d346773212b208380f71885979f93da65f07ea6
Author: veedrin <[email protected]>
Date:   Sat Dec 29 11:56:41 2018 +0800
    b.md
commit cde34665b49033d7b8aed3a334c3e2db2200b4dd
Author: veedrin <[email protected]>
Date:   Sat Dec 29 11:54:59 2018 +0800
    a.md
Copy the code

To see the specific changes made to each commit, add the -p parameter, which stands for –patch.

$ git log-p commit 7e2514419ec0f75d1557d3d8165a7e7969f08349 Author: veedrin <[email protected]> Date: Sat Dec 29 11:56:53 2018 +0800 c.md diff --git a/c.md b/c.md new file mode 100644 index 0000000.. e69de29 commit 4d346773212b208380f71885979f93da65f07ea6 Author: veedrin <[email protected]> Date: Sat Dec 29 11:56:41 2018 +0800 b.md diff --git a/b.md b/b.md new file mode 100644 index 0000000.. e69de29 commit cde34665b49033d7b8aed3a334c3e2db2200b4dd Author: veedrin <[email protected]> Date: Sat Dec 29 11:54:59 2018 +0800 a.md diff --git a/a.md b/a.md new file mode 100644 index 0000000.. e69de29Copy the code

You can also control the display of recent entries.

$ git log-p -1 commit 7e2514419ec0f75d1557d3d8165a7e7969f08349 Author: veedrin <[email protected]> Date: Sat Dec 29 11:56:53 2018 +0800 c.md diff --git a/c.md b/c.md new file mode 100644 index 0000000.. e69de29Copy the code

-p is a bit redundant, but you can use the –stat argument if you want to see statistics on file changes.

$ git log --stat

commit 7e2514419ec0f75d1557d3d8165a7e7969f08349
Author: veedrin <[email protected]>
Date:   Sat Dec 29 11:56:53 2018 +0800
    c.md
 c.md | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
commit 4d346773212b208380f71885979f93da65f07ea6
Author: veedrin <[email protected]>
Date:   Sat Dec 29 11:56:41 2018 +0800
    b.md
 b.md | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
commit cde34665b49033d7b8aed3a334c3e2db2200b4dd
Author: veedrin <[email protected]>
Date:   Sat Dec 29 11:54:59 2018 +0800
    a.md
 a.md | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
Copy the code

Still feel redundant? Just to see the submission instructions, there is one — Oneline can help you.

$ git log--oneline 4AD50f6 (HEAD -> master) add c.mod file 4d34677 Add B.mod file cde3466 Add a.mod fileCopy the code

To see a tree chart of Git commit history in a command line tool, use the –graph parameter.

$ git log --graph

* commit 7e2514419ec0f75d1557d3d8165a7e7969f08349 (HEAD -> master)
| Author: veedrin <[email protected]>
| Date:   Sat Dec 29 11:56:53 2018 +0800
|     c.md
* commit 4d346773212b208380f71885979f93da65f07ea6
| Author: veedrin <[email protected]>
| Date:   Sat Dec 29 11:56:41 2018 +0800
|     b.md
* commit cde34665b49033d7b8aed3a334c3e2db2200b4dd
  Author: veedrin <[email protected]>
  Date:   Sat Dec 29 11:54:59 2018 +0800
      a.md
Copy the code

I know you’ll find –graph and –oneline much better to eat.

$ git log --graph --oneline

* 7e25144 (HEAD -> master) c.md
* 4d34677 b.md
* cde3466 a.md
Copy the code

11) position

When the program encounters a bug, we need to locate it quickly.

There are two kinds of localization, the first is to locate which commit the bug is on, and the second is to locate who recently committed a particular line of a particular file.

bisect

Sometimes we find a bug in a program, but rolling back several versions does not solve the problem. This indicates that the bug was caused by a very old commit, but it was not noticed at the time.

So what to do? Continue version-by-version rollback?

I guess Linus Torvalds would despise you.

In order to focus on your work and not be distracted from looking down on you, Linus Torvalds has built a set of commands into Git to locate bugs.

You’ve all played the number game. The host quietly wrote down a number, gave everyone a number range, and then everyone took turns to start cutting, who cut the number written by the host will be fined three cups.

Yeah, that’s the dichotomy. The git command that exploits the binary bitwise bug is git bisect.

use

Suppose the current history of git projects looks like this.

C0 -- C1 -- C2 -- C3 -- C4 -- C5 -- C6 -- C7 -- C8 -- C9(HEAD -> master)
Copy the code

There was a commit that hid a bug, but fortunately, you don’t know which one.

Run git bisect start followed by the latest commit and the oldest commit in the range you want to locate.

$ git bisect start HEAD C0

Bisecting: 4 revisions left to test after this (roughly 2 steps)
[ee27077fdfc6c0c9281c1b7f6957ea2b59a461dd] C4
Copy the code

Then you see that the HEAD pointer automatically points to C4commit. If the range is odd bits, take the middle. If the range is even bits, take the older commit, such as C4commit here.

$ git bisect good

Bisecting: 2 revisions left to test after this (roughly 1 step)
[97cc0e879dc09796bd56cfd7c3a54deb41e447f6] C6
Copy the code

After the HEAD pointer points to C4commit, you should run the program. If there is no problem, there is a buggy commit after it. We just need to tell Git that the current commit and the older commit are all good.

The HEAD pointer then automatically points to C6commit.

Continuing to run the program at C6commit results in a recurrence of the bug. The problem is between C6commit and C4commit.

$ git bisect bad

Bisecting: 0 revisions left to test after this (roughly 0 steps)
[a7e09bd3eab7d1e824c0338233f358cafa682af0] C5
Copy the code

After marking C6commit as bad, the HEAD pointer automatically points to C5commit. If you run the program again, you can still reproduce the bug. Without further ado, mark C5commit as bad.

$ git bisect bad

a7e09bd3eab7d1e824c0338233f358cafa682af0 is the first bad commit
Copy the code

Since there is no dichotomy between C4commit and C5commit, Git will tell you that C5commit is the earliest commit you marked as bad. The problem should be C5commit.

git bisect reset

Previous HEAD position was a7e09bd... C5
Switched to branch 'master'
Copy the code

Now that you’ve found the problem, you can exit the Git bisect tool.

Git bisect Old has the same effect as Git bisect good, and Git bisect new has the same effect as Git bisect bad. Good bad is a little awkward.

regret

Git bisect is powerful, but if I’ve bisect several times and accidentally marked a GoodCommit as bad, or vice versa, should I reset?

Git Bisect also has a log command. You only need to save the bisect log to a file, then erase the error marked log from the file, and then restart Bisect with the new log.

git bisect log > log.txt
Copy the code

This command is used to save logs to log. TXT.

Look at the contents of the log.txt file.

# bad: [4d5e75c7a9e6e65a168d6a2663e95b19da1e2b21] C9
# good: [c2fa7ca426cac9990ba27466520677bf1780af97] add a.md
git bisect start 'HEAD' 'c2fa7ca426cac9990ba27466520677bf1780af97'
# good: [ee27077fdfc6c0c9281c1b7f6957ea2b59a461dd] C4
git bisect good ee27077fdfc6c0c9281c1b7f6957ea2b59a461dd
# good: [97cc0e879dc09796bd56cfd7c3a54deb41e447f6] C6
git bisect good 97cc0e879dc09796bd56cfd7c3a54deb41e447f6
Copy the code

Remove the mislabeled content.

# bad: [4d5e75c7a9e6e65a168d6a2663e95b19da1e2b21] C9
# good: [c2fa7ca426cac9990ba27466520677bf1780af97] add a.md
git bisect start 'HEAD' 'c2fa7ca426cac9990ba27466520677bf1780af97'
# good: [ee27077fdfc6c0c9281c1b7f6957ea2b59a461dd] C4
git bisect good ee27077fdfc6c0c9281c1b7f6957ea2b59a461dd
Copy the code

Then run git bisect replay log.txt.

$ git bisect replay log.txt

Previous HEAD position was ad95ae3... C8
Switched to branch 'master'
Bisecting: 4 revisions left to test after this (roughly 2 steps)
[ee27077fdfc6c0c9281c1b7f6957ea2b59a461dd] C4
Bisecting: 2 revisions left to test after this (roughly 1 step)
[97cc0e879dc09796bd56cfd7c3a54deb41e447f6] C6
Copy the code

Git will re-bisect from scratch based on the log, and the error marks will be erased.

Then it’s a matter of turning over a new leaf.

blame

In a collaborative project, each file may have been changed by more than one person. When something goes wrong, people want to quickly know who made the last change to a line of a file so they can sort out the blame.

Git Blame is one such command. This command is used to blame.

Git Blame only applies to a single file.

$git blame a.m. 705D9622 (veedrin 2018-12-25 10:09:04 +0800 1) $git blame a.m. 705D9622 (veedrin 2018-12-25 10:16:44 +0800 2 A65b29bd (veedrin 2018-12-25 10:19:05 +0800 4) a7e09bd3 (veedrin 2018-12-25 10:19:05 +0800 4 2018-12-25 10:19:19 +0800 5) įŽŽ 5 行 97CC0E87 (2018-12-25 10:21:55 +0800 6) įŽŽ 6 行 67029a81 (2018-12-25 10:19:19 +0800 6 Lisi 2018-12-25 10:23:37 +0800 9) lisi 2018-12-25 10:23:37 +0800 9) lisi 2018-12-25 10:23:37 +0800 9) lisi 2018-12-25 10:23:37 +0800 9 Line 9Copy the code

It lists the modifier information for each row.

The first part is the COMMIT hash value, indicating that the last modification of this row belongs to that commit.

The second part is the author and revision time.

The third part is the content of line.

If the file is too long, we can cut some lines.

$git blame -l 1,5 A.M.D 705d9622 A65b29bd (Bob 2018-12-25 10:17:02 +0800 3) ee27077F (veedrin 2018-12-25 10:19:05 +0800 4 2018-12-25 10:19:19 +0800 5) line 5Copy the code

Or I could write it this way.

$git blame -l 1,+4 A.M.D 705d9622 (veedrin 2018-12-25 10:09:04 +0800 1) Ee27077f (veeDRin 2018-12-25 10:19:05 +0800 4) įŽŽ 4 行Copy the code

But it didn’t turn out the way you expected. 1,+4 exactly means to start at 1 and display 4 lines.

If someone has the same name, you can display the email address to distinguish them. Add -e or –show-email.

$ git blame -eA.d 705D9622 ([email protected] 2018-12-25 10:09:04 +0800 1) Line 1 74EFF2ee ([email protected] 2018-12-25 10:16:44 +0800 2) line 2 A65b29bd ([email protected] 2018-12-25 10:17:02 +0800 3) ee27077F ([email protected] 2018-12-25 10:19:05 +0800 4) a7E09bd3 ([email protected] 2018-12-25 10:19:19 +0800 5) 97CC0E87 ([email protected] 2018-12-25 10:21:55 +0800 6) 67029A81 ([email protected] 2018-12-25 10:22:15 +0800 7) Ad95AE3F ([email protected] 2018-12-25 10:23:20 +0800 8) 4D5E75C7 ([email protected] 12-25 10:23:37 +0800 9Copy the code

12) tag

Git is a version management tool, but there are certainly some versions that are important, and you want to label those particular versions. For example, after a year of release, the features of the application are stable enough that v1.0 can be released by Christmas. This v1.0 can be implemented with tags in Git.

Git tags are divided into two types, lightweight tags and annotated tags.

A lightweight tag is the same as a branch, just a pointer to commit. But it can’t be switched, and once it’s attached, it can’t be moved.

An annotated tag is what we think of as a tag, which is a separate Git object. Include label name, email address and date, and label description.

create

The command to create lightweight tags is as simple as running git tag

.

$git tag v0.3Copy the code

There is a pointer file in the.git directory.

The git/refs/tags/v0.3Copy the code

To create annotated tags, add the -a parameter, which is short for –annotated.

$ git tag -a v1.0
Copy the code

Just like git commit, if you do not add the -m parameter, a default or custom editor will pop up asking you to write a tag description.

Don’t write?

fatal: no tag message?
Copy the code

After creating the annotation tag, the.git directory will have two more files.

The git/refs/tags/v0.3Copy the code
.git/objects/80/e79e91ce192e22a9fd860182da6649c4614ba1
Copy the code

The annotated tag creates not only a pointer, but also a tag object.

We’ve seen that Git has four object types, and the tag type is the last one we’ve seen.

Let’s look at the type of the object.

$ git cat-file -t 80e79e9

tag
Copy the code

Take a look at the contents of this object.

$ git cat-file -p 80e79e9

object 359fd95229532cd352aec43aada8e6cea68d87a9
typeCommit tag v1.0 tagger veedrin <[email protected]> 1545878480 +0800 v1.0Copy the code

It is associated with a COMMIT object that contains the name of the tag, who labeled it, when the tag was labeled, and the tag description.

Can I label the history commit? B: Sure.

$ git tag -a36 ff0f5 v1.0Copy the code

Just add the commit checksum after it.

To view

To view the tag list of the current Git project, run the git tag command without any parameters.

$git tag v0.3 v1.0Copy the code

Note that git tags are arranged alphabetically, not chronologically.

And I didn’t find a way to look at lightweight tabs and annotated tabs separately.

Git show

$git show v0.3 commit 36 ff0f58c8e6b6a441733e909dc95a6136a4f91b (tag: v0.3) Author: veedrin < [email protected] > Date: Thu Dec 27 11:08:09 2018 +0800 add a.md diff --git a/a.md b/a.md new file mode 100644 index 0000000.. e69de29Copy the code
$git show v1.0 tag v1.0 tag: veedrin <[email protected]> Date: Thu Dec 27 11:08:39 2018 + 0800 edition v1.0 commit 6 dfdb65ce65b782a6cb57566bcc1141923059d2b (HEAD - > master, the tag: V1.0) Author: Veedrin <[email protected]> Date: Thu Dec 27 11:08:33 2018 +0800 add b.md diff --git a/b.md b/b.md new file mode 100644 index 0000000.. e69de29Copy the code

delete

Git tags can’t be moved, but they can be deleted.

$ git tag -dV0.3 does tag'v0.3' (was 36ff0f5)
Copy the code

If the label has already been pushed to the remote end, it can also be deleted.

$ git push origin -dV0.3 To github.com: veedrin/git. Git - [does] v0.3Copy the code

push

By default, a Git push to a remote repository does not push tags up as well. Git push origin

if you want to push the tag to be shared remotely, you have to explicitly run git push origin

.

$git push Origin v1.0 Counting Objects: 1, done. 100% (1/1), 160 bytes | 160.00 KiB/s, done. Total 1 (delta 0), Reused zero (0) delta To github.com: veedrin/git. Git * [new tag] v1.0 - > v1.0Copy the code

There is no distinction between lightweight tags and annotated tags.

It is also possible to push a local label to a remote repository once.

$ git push origin --tags
Copy the code

This article is part of a series on horseshoe Git, with more to come

GitHub address (constantly updated) : github.com/veedrin/hor…

Blog address (the layout is really beautiful) : matiji.cn

If you think it will help you, please come to GitHub to Star or come to my blog to tell me personally

Git Overview

🎖 add

🎖 commit

🎖 branch

🎖 checkout

🎖 merge

🎖 rebase

🎖 reset

🎖 revert

🎖 stash

🎖 view

🎖 position

🎖 tag

🎖 remote