Git 是目前最流行、最好用的版本控制系统,在它的基础之上催生出了 GitHub 和 GitLab 这两个当前最流行的代码托管平台。本文梳理了一些常用的Git命令。

Git(读音为/gɪt/。)是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

git整个工作流的whole picture可以参考下面这张图片:

image-20190831105613619

图中的各部分分别表示:

一些基本概念:

image-20190831105455392

1.Quick Start

1. 配置用户信息

第一步需要配置你的git用户信息

$ git config --global user.name "yourname"
$ git config --global user.email youremail@example.com

如果使用了 --global 选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, git 都会使用那些信息。

当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运 行没有 --global 选项的命令来配置。

配置为之后,可以用以下命令查看:

 git config --list

注意:github的提交记录,需要你本地配置的email被加入到了github设置中才会记录为有效提交。

详情参考:

2. 初始化

把普通目录改成用git管理的仓库。目录下会多了一个.git的目录,这个目录是git来跟踪管理版本的

$ git init

3. 常用命令

下面这几个命令是最最最常用,最最最基本的git命令,可以先走一遍流程。

git status # 查看目前文件夹git的状态

git add . # 将本地所有的修改添加到暂存区

# git checkout -- <file> # 放弃对某个文件的修改

git commit -m "your description"
git push -u origin master # the first time with -u
# git push origin <要推送的本地分支名>

4. 查看版本库的更新过程

可以用log查看版本库的更新过程:

git log
# 查看各版本号及信息(所有的commit:本地commit + 其他同事的commit)

git log –pretty=oneline
git log --graph --pretty=oneline --abbrev-commit
# --pretty=oneline:一行显示,只显示哈希值和提交说明(--online本身也可以作为单独的属性)
# --abbrev-commit:仅显示SHA-1的前几个字符,而非所有的40个字符

2. 常用的撤销或回退操作

人非圣贤孰能无过,在执行了不想执行的git add 或者 git commit 或者 git push操作,我们需要执行相应的撤销操作。

2.1 git add的撤销

用命令git reset HEAD <file>可以把暂存区的修改撤销掉(unstage),重新放回工作区:

$ git reset HEAD readme.txt
Unstaged changes after reset:
M    readme.txt

git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本(上一个commit)。

2.2 直接丢弃在工作区对某个文件做的修改
git reset --hard HEAD # 撤销工作目录中所有未提交文件的修改内容
git checkout -- readme.txt  # 撤销工作目录中制定的未提交文件的修改内容

意思就是,把readme.txt文件在工作区做的修改全部撤销,这里有2种情况: 1. 如果 readme.txt修改后还没有放到暂存区,使用撤销修改就回到和版本库一模一样的状态(last commit)。 2. 另外一种是readme.txt已经放入暂存区了,接着又作了修改,撤销修改就回到添加暂存区后的状态。但一旦执行了git commit -m "*",就不能再使用上面的命令回退。

如果文件已经提交到了暂存区,可以采取以下两个步骤放弃修改。

git reset HEAD <file> # to unstage, 撤销git add 
git checkout -- <file> 

git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。所以git checkout -- [file] 是一个危险的命令。 你对那个文件做的任何修改都会消失 - 你只是拷贝了另一个文件来覆盖它。 除非你确实清楚不想要那个文件了,否则不要使用这个命令。

2.3 git commit撤销(没有push)
git reset --hard HEAD~1 # git reset --hard HEAD^
# 撤销之前的提交:
# 因为你使用了 --hard,所有你的文件将重置到上一次提交时的状态。
# uncommit for reworking: 去掉"--hard",这个对于非merge的commit有效,对于merge的commit最好删除merge后重新创建。

git reset --soft HEAD~1 # git reset --soft HEAD^
# 撤销提交但保留文件和索引:
# 从当前分支移除上一次commit,但是当前文件的修改会保留在你的工作区(working tree),同时暂存区(index)的修改也会保留。
# 此时直接用git commit就可以回到git reset之前的状态。

git reset HEAD~1 # git reset HEAD^
# 要撤销提交但保留更改:mixed mode,与soft mode非常像。
# 但是只把更改保留在工作区(working tree)而非暂存区(index),此时需要先git add然后git commit即可回到git reset之前的状态。

注意:git rest --hard HEAD~1的操作非常危险,本地工作区如果有unstaged的修改就会丢失,所以在使用这个命令前一定要保证本地没有unstaged files,即git status是空的! 所幸:git reset --keep HEAD~1可以作为git rest --hard HEAD~1的安全版本。

image-20190831123230822

如果某次commit后发现漏了一个文件,可以使用下面命令把这个文件加上上一次的commit中。

$ git add missing_file
$ git commit --amend --no-edit   # "--no-edit": 不编辑, 直接合并到上一个 commit
$ git log --oneline    # "--oneline": 每个 commit 内容显示在一行
2.4 git push的撤销
对于已经push的版本,进行回退
# step 1,本地回退到指定的版本
git reset --hard 版本号

# step 2,将远程的也回退到指定版本
git push  -f origin dev    
2.5 版本回退
# 回到上一个版本
git reset --hard HEAD^ 
# HEAD指向的版本就是当前版本
# 或者使用 git reset --hard HEAD~ 
# ------------------------------ #
# 如果想回退到100个版本:
git reset –-hard HEAD~100
# 本地回退到指定的版本
git reset --hard 版本号 
# ------------------------------ #
# 查看以往版本号(本地的commit)
git log
# 如果要回到未来的版本可以用这个查看
git reflog
2.6 git强制覆盖本地代码(与git远程仓库保持一致)
$ git fetch --all
$ git reset --hard origin/master 
$ git pull

3. 远程操作

Git远程操作详解: git pull/git fetch/git push等。

3.1 git pull
$ git pull <远程主机名> <远程分支名>:<本地分支名>

比如,要取回origin主机的next分支,与本地的master分支合并,需要写成下面这样 -

$ git pull origin next:master

# 相当于:
$ git fetch origin next
$ git merge origin/next

如果远程分支是与当前分支合并,则冒号后面的部分可以省略。

$ git pull origin next

在某些场合,Git会自动在本地分支与远程分支之间,建立一种追踪关系(tracking)。比如,在git clone的时候,所有本地分支默认与远程主机的同名分支,建立追踪关系,也就是说,本地的master分支自动”追踪”origin/master分支。

Git也允许手动建立追踪关系。

git branch --set-upstream master origin/next 
# 新版本git已经不支持该命令,改为:
git branch --set-upstream-to=origin/master master

上面命令指定master分支追踪origin/next分支。

如果当前分支与远程分支存在追踪关系,git pull就可以省略远程分支名。

$ git pull origin

上面命令表示,本地的当前分支自动与对应的origin主机”追踪分支”(remote-tracking branch)进行合并。

如果当前分支只有一个追踪分支,连远程主机名都可以省略。

$ git pull

上面命令表示,当前分支自动与唯一一个追踪分支进行合并。

如果合并需要采用rebase模式,可以使用–rebase选项。

$ git pull --rebase <远程主机名> <远程分支名>:<本地分支名>

如果远程主机删除了某个分支,默认情况下,git pull不会在拉取远程分支的时候,删除对应的本地分支。这是为了防止,由于其他人操作了远程主机,导致git pull不知不觉删除了本地分支。

但是,你可以改变这个行为,加上参数 -p 就会在本地删除远程已经删除的分支。

$ git pull -p
# 等同于下面的命令
$ git fetch --prune origin 
$ git fetch -p
3.2 git fetch
$ git fetch <远程主机名>

默认情况下,git fetch取回所有分支(branch)的更新。如果只想取回特定分支的更新,可以指定分支名。

$ git fetch <远程主机名> <分支名>

比如,取回origin主机的master分支。

$ git fetch origin master

所取回的更新,在本地主机上要用”远程主机名/分支名”的形式读取。比如origin主机的master,就要用origin/master读取。

3.3 git push
$ git push <远程主机名> <本地分支名>:<远程分支名>
$ git push origin master

上面命令表示,将本地的master分支推送到origin主机的master分支。如果后者不存在,则会被新建。

如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。

$ git push origin :master
# 等同于
$ git push origin --delete master

上面命令表示删除origin主机的master分支。

如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略。

$ git push origin

上面命令表示,将当前分支推送到origin主机的对应分支。

如果当前分支只有一个追踪分支,那么主机名都可以省略。

$ git push

如果当前分支与多个主机存在追踪关系,则可以使用-u选项指定一个默认主机,这样后面就可以不加任何参数使用git push。

$ git push -u origin master

上面命令将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以不加任何参数使用git push了。

$ git push --force origin 

上面命令使用–force选项,结果导致远程主机上更新的版本被覆盖。

4. 分支管理

branch management

A successful Git branching model

Understanding the Git Workflow

4.1 概述

并不是一定要把本地分支往远程推送:

4.2 git tag

Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。轻量级标签就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。而含附注标签(git tag -a ),实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。一般我们都建议使用含附注型的标签,以便保留相关信息;当然,如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。创建一个含附注类型的标签非常简单,用git tag -aannotated 的首字母)指定标签名字即可。

注意:标签总是和某个commit挂钩。如果这个commit既出现在master分支,又出现在dev分支,那么在这两个分支上都可以看到这个标签。

后期加注标签:

# 后期加注标签,在提交 “updated rakefile” 后为此项目打上版本号 v1.2
$ git log --pretty=oneline
15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch 'experiment'
a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support
0d52aaab4479697da7686c15f77a3d64d9165190 one more thing
6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch 'experiment'
0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function
4682c3261057305bdd616e23b64b0857d832627b added a todo file
166ae0c4d3f420721acbb115cc33848dfcc2121a started write support
9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile
964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo
8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme

$ git tag -a v1.2 9fceb02

默认情况下,git push 并不会把标签传送到远端服务器上,只有通过显式命令才能分享标签到远端仓库。其命令格式如同推送分支,运行 git push origin [tagname] ,比如:

$ git push origin v1.5

如果要一次推送所有本地新增的标签上去,可以使用 --tags 选项:

$ git push origin --tags
4.3 master和dev分支

git clone 默认会把远程仓库整个给clone下来,但只会在本地默认创建一个master分支,可以使用checkout命令来把远程分支取到本地。

git clone ...
git checkout -b dev origin/dev
# 或者使用-t参数,它默认会在本地建立一个和远程分支名字一样的分支
git checkout -t origin/dev

git branch

git branch -r # 查看远程分支
git branch -a # 查看所有分支
# Git创建Develop分支的命令:
git checkout -b develop master

# 将Develop分支发布到Master分支的命令:
# 切换到Master分支
git checkout master

# 对Develop分支进行合并
git merge --no-ff develop
# 可能需要进行一下分支的关联,指定本地dev分支与远程origin/dev分支的链接:
$ git branch --set-upstream-to=origin/dev dev
4.4 Fast-Forward

当前分支合并到另一分支时,如果没有分歧解决,就会直接移动文件指针。这个过程叫做fastforward。

举例来说,开发一直在master分支进行,但忽然有一个新的想法,于是新建了一个develop的分支,并在其上进行一系列提交,完成时,回到 master分支,此时,master分支在创建develop分支之后并未产生任何新的commit。此时的合并就叫fast forward。

About parameter: --no-ff image-20190901010617970

4.5 临时性分支

Master和Develop是版本库的主要分支。前者用于正式发布,后者用于日常开发。常设分支只需要这两条就够了。

但是,除了常设分支以外,还有一些临时性分支,用于应对一些特定目的的版本开发。临时性分支主要有三种:

4.6 git stash

具体操作时,可能会用到 git stash

1、储藏更改:将当前更改的代码储藏起来,等以后恢复使用

git stash

2、恢复储藏的代码

git stash pop 
//恢复的同时把stash内容删掉

或者

git stash apply  
//恢复stash,但是stash内容并不删除

git stash drop 
//在上面操作的基础上,以此来删除stash

注: git stash list : 查看全部的stash列表。

3、将stash空间清空

git stash clear

4、git stash popgit stash apply 区别

原来git stash pop stash@{id}命令会在执行后将对应的stash idstash list里删除,而 git stash apply stash@{id} 命令则会继续保存stash id

4.7 Example
git checkout -b newbranch # create a new branch
# do something...
git add .
git commit -m "do sth"
git checkout master
git merge --no-ff -m "merge new branch" newbranch # merge with no fast forward 
git branch -d newbranch
git branch
git push origin master
git push origin dev

# 将本地分支branch1推到远端的branch2:
git push origin branch1:branch2

# 删除分支
git branch -D issues1234  //本地强制删除分支issues1234    
git push origin  :issues1234  //推到远程

master分支是主分支,因此要时刻与远程同步。一些修复bug分支不需要推送到远程去,可以先合并到主分支上,然后把主分支master推送到远程去。

把远程的origin的dev分支到本地来,可使用命令创建本地dev分支:

git checkout  –b dev origin/dev

5. Conflicts

发生冲突的文件

<<<<<<< HEAD

Creating a new branch is quick & simple.

=======

Creating a new branch is quick AND simple.

>>>>>>> feature1

其中,git使用 <<<<<<<=======>>>>>>> 标记文件中自己和别人产生冲突的部分。

<<<<<<<======= 之间为自己的代码,先出现;

=======>>>>>>> 之间为别人的代码,后出现。

6. diff

要查看尚未暂存的文件更新了哪些部分,不加参数直接输入git diff

$ git diff

此命令比较的是工作目录中当前文件和暂存区域快照之间的差异, 也就是修改之后还没有暂存起来的变化内容。

如果暂存了,用git diff就没有反应了。

若要查看已暂存的将要添加到下次提交里的内容,可以用 git diff --cached 命令。(Git 1.6.1 及更高版本还允许使用 git diff --staged,效果是相同的,但更好记些。)

总结:
git diff # shows unstaged changes.
git diff --cached # shows staged changes.
git diff HEAD # shows all changes (both staged and unstaged).
版本之间的差异
# 上次提交:HEAD^
git diff HEAD^ HEAD

# As of Git 1.8.5, @ is an alias for HEAD, so you can use:
git diff @~..@

#The following will also work:
git show

# If you want to know the diff between head and any commit you can use:
git diff commit_id HEAD

# And this will launch your visual diff tool (if configured):
git difftool HEAD^ HEAD

# Since comparison to HEAD is default you can omit it (as pointed out by Orient):

git diff @^
git diff HEAD^
git diff commit_id

7. submodule

https://www.cnblogs.com/nicksheng/p/6201711.html

https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%AD%90%E6%A8%A1%E5%9D%97

REFERENCE

git cheat sheet

GitHub 漫游指南 – GitHub 漫游指南

git - 简明指南

廖雪峰git

GIT tutorial

GIT PRO BOOK (Chinese)

git tutorial

how to teach git

Follow these simple rules and you’ll become a Git and GitHub master

Git重要概念与常用命令(中文版,速查)

Implementing Git in Data Science

阮一峰的git教程:

Git远程操作详解

Git 使用规范流程

常用 Git 命令清单

Git 工作流程

Git分支管理策略

detached_head