Git复习

Posted by Jackson Blog on June 23, 2020

1. Getting Started

What is Git?

  • Snapshots,Not Difference

    Git thinks of its data more like a series of snapshots of a miniature filesystem. To be efficient, if files have not changed, Git doesn’t store the file again,just a linke to the previous identical file it has already stored.

    Git thinks about its data more like a stream of snapshots

  • Nearly Every Operation Is Local

  • Git Has Integrity

    SHA-1 Hash

  • Git Generally Only Adds Data
  • The Threee States
    • Working Directory modified
    • Staging Area staged
    • .git directory(Repository) :metadata and object database committed

2. Git Basics

Getting a Git Repository

  1. local directory
  2. clone
$ git init

This creates a new subdirectory named .git that contains all of your necessary repo files — a Git repo skeleton

Recording Changed to the Repository

Each file in your working directory can be in one of two stated

  • Tracked File
    • Modified
    • Unmodified
    • Staged
  • Untracked File: any files in your woring directory that were not in your last snapshot and are not in your staging area.

Short Status

$ git status -s
 M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt

​ New files that aren’t tracked have a ?? next to them, new files that have been added to the staging area have an A, modified files have an M and so on. There are two columns to the output — the left-hand column indicates the status of the staging area and the right-hand column indicates the status of the working tree. So for example in that output, the README file is modified in the working directory but not yet staged, while the lib/simplegit.rb file is modified and staged. The Rakefile was modified, staged and then modified again, so there are changes to it that are both staged and unstaged.

Ignoring Files

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

In the simple case, a repository might have a single .gitignore file in its root directory, which applies recursively to the entire repository. However, it is also possible to have additional .gitignore files in subdirectories. The rules in these nested .gitignore files apply only to the files under the directory where they are located. (The Linux kernel source repository has 206 .gitignore files.)

View Your Staged and Unstaged Changes

$ git diff

​ That command compares what is in your working directory with what is in your staging area.

​ It’s important to note that git diff by itself doesn’t show all changes made since your lastcommit — only changes that are still unstaged. If you’ve staged all of your changes, git diff will give you no output.

$ git diff --staged

That command compares your staged changes to your last commit

Skipping the Staging Area

$ git commit -am "xx"

Removing Files

$ git rm

remove a file from your staging area. the git rm does that, and also removes the file from your working directory so you don’t see it as an untracked file the next time around.

$ git rm --cached xxx

keep the file in your working tree but remove it from your staging area

Moving Files

if you rename a file in Git, no metadata is stored in Git that tells it you renamed the file. However, Git is pretty smart about figuring that out after the fact

$ git mv file_from file_to

this is equivalent to running something like this

$ mv file_from file_to
$ git rm file_from
$ git add file_to

**Git figure out that it’s a rename implicitly, so it doesn’t matter if you rename a file that way or with the mv command **

View the Commit History

$ git log -p -num
num like 2,3,4...
$ git log --stat

See some abbreviated stats for each commit

image-20200624165619594

Difference between Author and Committer

​ You may be wondering what the difference is between author and committer. The author is the person who originally wrote the work, whereas the committer is the person who last applied the work. So, if you send in a patch to a project and one of the core members applies the patch, both of you get credit — you as the author, and the core member as the committer. We’ll cover this distinction a bit more in Distributed Git.

Limiting Log Output

image-20200624165556762

--no-merges prevent the display of merge commits cluttering up your log history

Undoing Things

​ When forget to add some files, or mess up your commit message. If you want to redo that commit, make the additional changes you forgot, stage them, and commit again using --amend option

$ git commit --amend

​ This command takes your staging area and uses it for the commit. If you’ve made no changes since your last commit (for instance, you run this command immediately after your previous commit), then your snapshot will look exactly the same, and all you’ll change is your commit message. The same commit-message editor fires up, but it already contains the message of your previous commit. You can edit the message the same as always, but it overwrites your previous commit.

Unstaging a Staged File

$ git reset HEAD <file>

Unmodifying a Modified File

$ git checkout -- <file>

Git just replaced that file with the most recently-committed version.

Working with Remotes

$ git remote add <shortname> <url>

Fetching and Pulling from your remotes

$ git fetch <remote>

The command goes out to that remote project and pulls down all the data from that remote project that you don’t have yet. After you do this, you should have references to all the branches from that remote, which you can merge in or inspect at any time.

$ git pull

If your current branch is set up to track a remote branch (see the next section and Git Branching for more information), you can use the git pull command to automatically fetch and then merge that remote branch into your current branch. (branch to branch, it means you cannot pull down all the branch)

Pushing to Your Remotes

$ git push <remote> <branch>

Inspecting a Remote

$ git remote show

See more Infomation about a particular remote

git pull

it will automatically merge in the master branch on the remote after it fetches all the remote references.

Renaming and Removing Remotes

$ git remote rename 
$ git remote remove

Tagging

Creating Tags

Git supports two types if tags: lightweight and annotated

  • A lightweight tag is very much like a branch taht doesn’t change–its just a pointer to a specific commit

    $ git tag
    

    don’t support any of the -a,-s or -m options.

  • Annotated tags,owever, are stored as full objects in the Git database. They’re checksummed; contain the tagger name, email, and date; have a tagging message; and can be signed and verified with GNU Privacy Guard (GPG).

    $ git tag -a xxx -m "xxx"
    

You can see the tag data along with the commit that was tagged by using the git show command:

$ git show

Tagging Later

$ git tag <-a> xxx xxxx(checksum)

Sharing Tags

By default, the git push command doesn’t transfer tags to remote servers.

​ You will have to explicitly push tags to a shared server after you have created them. This process is just like sharing remote branches — you can run git push origin .

​ If you have a lot of tags that you want to push up at once, you can also use the –tags option to the git push command. This will transfer all of your tags to the remote server that are not already there.

git push --tags will push both lightweight and annotated tags There is currently no option to push only lightweight tags, but if you use git push --follow-tags only annotated tags will be pushed to the remote.

Deleting Tags

To delete a tag on your local repository, you can use git tag -d .

Note that this does not remove the tag from any remote servers. There are two common variations

for deleting a tag from a remote server.

  • The first variation is git push :refs/tags/:

  • The second (and more intuitive) way to delete a remote tag is with:

    $ git push origin --delete <tagname>

image-20200625151938382

Git Branching

The way Git branches is incredibly lightweight, making branching operations nearly instantaneous, and switching back and forth between branches generally just as fast

Branches in a Nutshell

When you make a commit, Git stores a commit object that contains a pointer to the snapshot of the content you staged. This object also contains the author’s name and email address, the message that you typed, and pointers to the commit or commits that directly came before this commit (its parent or parents): zero parents for the initial commit, one parent for a normal commit, and multiple parents for a commit that results from a merge of two or more branches.

image-20200701091246558

Your Git repository now contains five objects: three blobs (each representing the contents of one of the three files), one tree that lists the contents of the directory and specifies which file names are stored as which blobs, and one commit with the pointer to that root tree and all the commit metadata.

A branch in Git is simply a lightweight movable pointer to one of these commits. The default branch name in Git is master. As you start making commits, you’re given a master branch that points to the last commit you made. Every time you commit, the master branch pointer moves forward automatically.

image-20200701091804731

Creating a New Branch

What happens when you create a new branch? Well, doing so creates a new pointer for you to move around

$ git branch <branchName>

move around. How does Git know what branch you’re currently on? It keeps a special pointer called HEAD. In Git, this is a pointer to the local branch you’re currently on

Because a branch in Git is actually a simple file that contains the 40 character SHA-1 checksum of

the commit it points to, branches are cheap to create and destroy. Creating a new branch is as quick

and simple as writing 41 bytes to a file (40 characters and a newline).

Switching Branches

$ git checkout

That command did two things.

  • It moved the HEAD pointer back to point to the master branch
  • it reverted the files in your working directory back to the snapshot that master points to. This also means the changes you make from this point forward will diverge from an older version of the project.

if Git cannot do it cleanly, it will not let you switch at all. (刚刚测试了一下,发现行径还是挺诡异的,最好checkout前stash一下,保持一个clean working state)

git log doesn’t show all the branches all the time

git log will only show commit history below the branch you’ve checked out.

To show commit history for the desired branch you have to explicitly specify it: git log .

To show all of the branches, add –all to your git log command.

  • fast-forward: Git simplifies things by moving the pointer forward because there is no divergent work to merge together
  • three-way merge: Git creates a new snapshot that results from this three-way merge and automatically creates a new commit that points to it. This is referred to as a merge commit, and is special in that it has more than one parent.

Basic Merge Coflicts

Branch Management

$ git branch

If you run it with no arguments, you get a simple listing of your current branches:

​ The useful --merged and --no-merged options can filter this list to branches that you have or have not yet merged into the branch you’re currently on. To see which branches are already merged into the branch you’re on, you can run git branch --merged:

$ git branch --no-merged

 testing

This shows your other branch. Because it contains work that isn’t merged in yet, trying to delete it

with git branch -d will fail:

$ git branch -d testing
error: The branch 'testing' is not fully merged.
If you are sure you want to delete it, run 'git branch -D testing'.

Branching Workflows

Long-Running Branches

Topic Branches

Remote Branches

​ Remote references are references(pointers) in your remote repo, including branches, tags, and so on.

​ Remote tracking branches are referencse to the state of remote branches. They are local references that you cann’t move.Git moves them for you whenever you do any network communication, to make sure they accurately represent the state of the remote repository. Think of them as bookmarks, to remind you where the branches in your remote repositories were the last time youconnected to them.

​ Remote-tracking branch names take the form /.

Pushing

$ git push <remote> <branch>

This is a bit of a shortcut. Git automatically expands the serverfix branchname out to refs/heads/serverfix:refs/heads/serverfix

It’s important to note that when you do a fetch that brings down new remote-tracking branches, you don’t automatically have local, editable copies of them.

Tracking Branches

​ Checking out a local branch from a remote-tracking branch automatically creates what is called a “tracking branch”

  • tracking branch : Tracking branches are local branches that have a direct relationship to a remote branch

    If you’re on a tracking branch and type git pull, Git automatically knows which server to fetch from and which branch to merge in.

  • upstream branch

$ git checkout -b <branch> <remote>/<branch>
	To set up a local branch with a different name than the remote branch, you can easily use the first version with a different local branch name:
$ git checkout --track <remote>/<branch>

$ git checkout <branch>
	If the branch name you’re trying to checkout (a) doesn’t exist and (b) exactly matches a name on only one remote, Git will create a tracking branch for you:

If you already have a local branch and want to set it to a remote branch you just pulled down, or want to change the upstream branch you’re tracking, you can use the -u or –set-upstream-to option to git branch to explicitly set it at any time.

$ git branch -u origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.

Upstream shorthand

When you have a tracking branch set up, you can reference its upstream branch with the @{upstream} or @{u} shorthand. So if you’re on the master branch and it’s tracking origin/master, you can say something like git merge @{u} instead of git merge origin/master if you wish.

If you want to see what tracking branches you have set up, you can use the -vv option to git branch. This will list out your local branches with more information including what each branch is tracking and if your local branch is ahead, behind or both

$ git branch -vv
  iss53 7e424c3 [origin/iss53: ahead 2] Add forgotten brackets
  master 1ae2a45 [origin/master] Deploy index fix
* serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] This should do it
  testing 5ea463a Try something new 

serverfix–> there is one commit on the server we haven’t merged in yet and three commits locally that we haven’t pushed

It’s important to note that these numbers are only since the last time you fetched from each server. This command does not reach out to the servers

Pulling

​ While the git fetch command will fetch all the changes on the server that you don’t have yet, it will not modify your working directory at all

Deleting Remote Branches

You can delete a remote branch using the –delete option to git push. If you want to

delete your serverfix branch from the server, you run the following:

$ git push origin --delete serverfix

To https://github.com/schacon/simplegit

 \- [deleted] serverfix

Basically all this does is remove the pointer from the server. The Git server will generally keep the data there for a while until a garbage collection runs, so if it was accidentally deleted, it’s often easy to recover.

Rebasing

The Basic Rebasing

Rebase: This operation works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn.

Rebasing replays changes from one line of work onto another in the order they were introduced, whereas merging takes the endpoints and merges them together.

The Perils of Rebasing

Do not rebase commits that exist outside your repository and that people may have based work on

$ git pull --rebase

Git Tools

Revision Selection

  • short sha-1

  • branch references

    If you want to see which specific SHA-1 a branch points to, or if you want to see what any of these

    examples boils down to in terms of SHA-1s, you can use a Git plumbing tool called rev-parse.

    $ git rev-parse <branchName>
    

Reflog Shortnames

“reflog” — a log of where your HEAD and branch references have been for the last few months.

It’s important to note that reflog information is strictly local — it’s a log only of what you’ve done in your repository

Ancestry References

^ Git resolves it to mean the parent of that commit.

~ refers to the first parent

Commit Ranges

Double Dot

This basically asks Git to resolve a range of commits that are reachable from one commit but aren’t reachable from another.

image-20200706085248261

Multiple Points

not

image-20200706085802682

Triple Dot

which specifies all the commits that are reachable by either of two references but not by both of them

$ git log --left-right master...experiment
< F			//master
< E			//master
> D			//experiment
> C			//experiment

image-20200706090352934

Reset Demystified

image-20200707083703066

  • Just think of HEAD as the snapshot of your last commit on that bracnh

  • The index is your proposed next commit

​ In fact, it’s pretty easy to see what that snapshot looks like. Here is an example of getting the actual directory listing and SHA-1 checksums for each file in the HEAD snapshot:

$ git cat-file -p HEAD

tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf

author Scott Chacon 1301511835 -0700

committer Scott Chacon 1301511835 -0700

initial commit

$ git ls-tree -r HEAD

100644 blob a906cb2a4a904a152... README

100644 blob 8f94139338f9404f2... Rakefile

040000 tree 99f1a6d12cb4b6f19... lib

image-20200707084501245