Git 版本控制 (命令行)

1,593 阅读9分钟
原文链接: blog.csdn.net

前言


Git这个版本控制工具,早在两三年前我就开始使用了。不过后来换了新东家后,又开始变成了svn,最近又切成git了。
通过近期的使用,遇到了一些坑,遂引发此文,以作记录

注:可以通过上面的目录,来选取自己感兴趣的内容,进行查看

下载安装


链接:
git-scm.com/download 下载首页
git-scm.com/download/wi…
git-scm.com/download/li…
git-scm.com/download/ma…
git-scm.com/downloads/g… GUI工具

mac下还可以通过brew安装,再配合zsh的git短命令插件,来玩。

下载安装好后,打开命令行,运行:git --version
查看版本号,以确保git可用。如提示找不到命令git,请将其安装目录下的可执行文件(git.exe 或 就叫git)父目录路径,添加到环境变量中。

生成密钥

命令:

ssh-keygen -t rsa -C "aa86799@163.com"

回车后会弹出提示:

Generating public/private rsa key pair.
Enter file in which to save the key (/Users/stone/.ssh/id_rsa):

意思是:创建公钥/私钥文件,请键入文件的保存地址。默认直接回车就会生成在/Users/stone/.ssh/ 下,私钥文件名:id_rsa,公钥文件名:id_rsa.pub。
密钥目录,一般都是在 users>user>.ssh 下。windows中为:C:\Users\Administrator.ssh

也可以在输入创建命令时直接指定目录:

ssh-keygen -t rsa -f ~/.ssh/id_rsa.github -C "aa86799@163.com"

回车后,会提示:

Enter passphrase (empty for no passphrase): 

意指,输入密码。不输,也可以,直接回车就行。然后会提示让你再输入一次密码。输入后,再回车,就会生成相应的密钥文件,在指定的目录位置。

注: 一般,可能我们需要多种git密钥,比如GitHub需要一个,公司需要一个,其它… 所以有必要指定不同的密钥文件名字加以区分。

查看公钥

一般我们要把公钥,上传到服务器。
找到.pub结尾的公钥文件。
以命令行打开(mac/Linux 用cat命令),或用文本文件打开,复制出来,上传即可
类似内容:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfCwawUX/Milxu29sH5kZPFWWPoe/
… aa86799@163.com

配置


设置user name 、email

设置Git的user name和email,用于显示在版本提交记录中:

git config --global user.name "stone"       
git config --global user.email "aa86799@163.com" 
--后可选 globalsystem、local

global 影响范围:系统用户下全局
system 影响范围:系统下全局
local 影响范围:当前项目下

我一般的做法是,进入项目目录后,采用local来配置。因为对应多个git服务端,我想设置不同的邮件和名字。

配置.ssh/config

上面的命令执行后,会在.ssh目录下生成一个config文件。当然,不执行命令,也可以在其下,手动创建一个名为config的空文件。

如下,我配置了github和oschina:

Host github.com
   IdentityFile ~/.ssh/id_rsa.github
   User git

Host oschina.net
   IdentityFile ~/.ssh/oschina_id_rsa
   User git

一些可选配置参数说明:

Host github.com #主机名
IdentityFile ~/.ssh/id_rsa.github #公钥文件
User git #连接服务器的用户名
hostname www.github.com #服务器ip地址,也可以是域名
Port 5566 #服务器端口号
RSAAuthentication yes # 采用RSA加密认证
PubkeyAuthentication yes #采用公钥认证

配置后,在本地与server的交互时,git才知道用哪一种具体的config。

git仓库


本地新建仓库

进入本地文件目录,一般就是某个项目的工程目录下,执行:

git init

会生成一个.git目录

关联远程仓库

比如关联github。
需要先在github上建立一个repository,比如名为:MyActionBar

再进入项目根目录,键入命令,关联远程仓库:

git remote add origin git@github.com:aa86799/MyActionBar.git

git clone远程仓库

比如clone 一个github上的项目,命令:

git clone https://github.com/googlesamples/android-architecture.git 
#直接clone出名为android-architecture的项目

后跟一个指定的项目名字:

git clone https://github.com/googlesamples/android-architecture.git myArchi #clone出一个名myArchi的项目

配置本地user name 、email

配置命令:

git config --local user.name "stone"        
git config --local user.email "aa86799@163.com" 

会在.git中的config文件中添加如下内容:

[user]
    name = stone
    email = aa86799@163.com

版本控制操作


以下罗列一些常用的命令操作与选项。

下文中, 符号[ ]表示,命令后跟的内容,实际使用时,不需要符号[ ]。

branch 分支

仓库建好后,默认为master分支

分支的好处:
如在svn中,每次开发一个新版本,都要新创建一个分支的目录;而在git中只要创建一个新的分支,即可。
还可以创建本地分支,进行自己的想要一些改动,只要不push到远程,就不会影响服务端项目。

分支相关常用命令:

  • 查看本地所有分支:
git branch
  • 查看本地和远程所有分支:
git branch -a

这里写图片描述

  • 创建本地分支(从当前分支最后commit):
git branch [newBranch]
git checkout -b [newBranch]  #创建并切换到新分支
git checkout -b [newBranch 9510eee]#从当前分支的某次commit记录为9510eee处,创建新分支,并切换到新分支
git branch [newBranch 9510eee] #从commit记录为9510eee处,创建新分支
  • 创建与远程分支相关联的本地分支:
#检出远程master分支 到 本地分支 newBranch
git branch [newBranch remote/master]
git checkout -b [newBranch remote/master] #创建并切换到新分支
  • 删除分支:
git branch -d [specifiedBranch]
  • 切换分支:
git checkout [branchName]

注:分支切换时,要注意,如果本地有修改未commit或stash,需要先commit或stash

tag 标签

一般,项目的每个release稳定版,都要求有一个tag标签

常用命令,

  • 显示当前所有标签:
git tag
  • 创建标签,并使用-m 添加说明信息:
git tag -a [v1.1] -m [v1.1 release version]
  • 删除指定标签:
git tag -d [tagName]
git tag -d -D [tagName]  #强制删除分支
  • 从一个标签检出一个新分支:
git checkout -b [branch_name tag_name]

status 文件版本状态

  • 查看项目中文件的变动状态:
git status

在输出信息中,会看到(这里只记录了开头修饰词):

new file 已加入版本记录,但未commit
modified 修改了版本记录中某文件
deleted 删除了版本记录中某文件
Untracked files 未加入版本记录(追踪)
…
  • 状态简览
git status -s

这里引用一个官方示例,输出如下:

$ git status -s
 M README    #M在右,前面空了一个字符位置
M  Rakefile  #M在左
A  lib/git.rb
M  lib/simplegit.rb
?? LICENSE.txt

说明:
?? —— Untracked file,未add到版本记录
A —— 已add (详见下面的add命令)
R —— rename,被重命名了
左M —— 新的修改,已add到版本记录
右M —— 新的修改,未add到版本记录,如要提交该修改,必须先add
左D 、右D —— 类似M,指文件删除记录,是否add到版本记录中

add 加入版本记录

将文件加入版本记录。配合status命令,将需要加入版本记录的,有选择的加入。

git add //后跟目录路径,或某个具体文件路径

ignore 忽略文件

将不需要加入版本记录的文件,进行ignore操作。
需要在仓库目录下建立一个 .gitignore 文件(其与.git是平级的)
配置语法:

  以斜杠“/”开头表示目录,以防止递归;可以以”/”结尾指定目录

  以星号“*”通配多个字符;

  以问号“?”通配单个字符

  以方括号“[]”包含单个字符的匹配列表;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)

  以叹号“!”表示不忽略(跟踪)匹配到的文件或目录;

git 对于 .ignore 配置文件是按行从上到下进行规则匹配的,意味着如果前面的规则匹配的范围更大,则后面的规则将不会生效

rm 移除文件跟踪

要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。 可以用 git rm 命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。

  • 不再追踪暂存区文件

如现有一个新文件PROJECTS.md,刚刚进行了add,还未提交过。

如果只是简单地从工作目录中手工删除(rm)文件,运行 git status 时就会在 “Changes not staged for commit” 部分(也就是 未暂存清单)看到:

$ rm PROJECTS.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
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:    PROJECTS.md

no changes added to commit (use "git add" and/or "git commit -a")

然后再运行 git rm 记录此次移除文件的操作:

$ git rm PROJECTS.md
rm 'PROJECTS.md'
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    deleted:    PROJECTS.md

下一次提交时,该文件就不再纳入版本管理了。

如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f(译注:即 force 的首字母)。 这是一种安全特性,用于防止误删还没有添加到快照的数据,这样的数据不能被 Git 恢复。场景:对于新文件,刚进行了add,然后又修改了该文件,然后想 用git rm 取消追踪,就需要-f选项了。

  • 从 Git 仓库中删除版本追踪
    另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。 换句话说,你想让文件保留在磁盘,但是并不想让 Git 继续跟踪。 当你忘记添加 .gitignore 文件,不小心把一个很大的日志文件或一堆 .a 这样的编译生成文件添加到暂存区时,这一做法尤其有用。 为达到这一目的,使用 –cached 选项:
git rm --cached README

git rm 命令后面可以列出文件或者目录的名字,也可以使用 glob 模式(指shell所使用的简化了的正则表达式)。 比方说:

g itrm log/ .lo g \ Git shel l log/ .l og git rm *~ 该命令为删除以 ~ 结尾的所有文件。

mv 移动文件,重命名

比如运行了下面三条命令:

$ mv README.md README
$ git rm README.md
$ git add README

Git 会意识到这是一次改名操作。
可以使用如下一条命令,来进行改名操作

git mv [file_from file_to]

commit 提交

  • 已add文件,commit

所有改动过的且已加入版本记录的文件,可以使用commit来提交到本地仓库。

git commit -m [message] [file1] [file2]

可以不写commit信息,一般还是建议写。
commit哪些文件是可以指定的,中间用空格分隔,需要文件的完整路径。
若不指定具体文件,默认为仓库下所有改动过的且已加入版本记录的文件

  • 之前已在版本记录中,修改后未add文件,想直接commit
git commit -a -m [message] [file1] [file2]

添加-a选项,即可

  • 撤销操作
    有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 –amend 选项的提交命令尝试重新提交:
git commit --amend

例如,你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:

$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend

最终你只会有一个提交 —— 第二次提交将代替第一次提交的结果。

注:其他撤销例子见reset

cherry-pick

push

fetch

拉取远程仓库上的所有新的信息。

git fetch [remote-name] #如果只关联了一个远程仓库,可以不用显式指定远程仓库名

pull

stash

reset、checkout 重置

  • HEAD
    HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交。 这表示 HEAD 将是下一次提交的父结点。 通常,理解 HEAD 的最简方式,就是将它看做 你的上一次提交 的快照。

  • 撤销add
    例如,你已经修改了两个文件并且想要将它们作为两次独立的修改提交,但是却意外地输入了 git add * 暂存了它们两个。 如何只取消暂存两个中的一个呢? git status 命令提示了你:

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
    modified:   CONTRIBUTING.md

提示中就说了,可以用如下命令来撤销某个add:

$ git reset HEAD CONTRIBUTING.md

这样,并不会丢失 CONTRIBUTING.md 文件的修改。

  • –hard选项
    将当前分支,重置到某次提交(只要是reglog中存在的都可以)。
git reset --hard 1a410efbd13591db07496601ebc7a059dd55cfe9
  • 用checkout命令来重置提交或文件

    1. 重置文件
      如有一个已在版本记录中的文件a,现在做了修改,然后add,突然又觉得它的修改不需要了,想重置回到最近记录的样子,可用命令:
git checkout [HEAD a] #HEAD指代最新提交,当然也可用SHA-1值。

如果将文件a恢复到上上个版本,只需要将HEAD换成上上个commit的SHA-1值。这时,通过git status,看到文件a被修改了,且自动add。

2.重置commit
就是将当前分支重置到某次commit,同时会生成一个临时分支,并切换到这个临时分支上。

git checkout [077d7ad] #commit记录:077d7ad 

如图:
这里写图片描述
这里还提示你可以后跟 -b 新分支名,来从该commit检出一个新分支。

log

查看提交历史

git log

会按提交时间列出所有的更新,最近的更新排在最上面。 这个命令会列出每个提交的 SHA-1 校验和、作者的名字和电子邮件地址、提交时间以及提交说明

  • –oneline选项
    一行输出,只显示SHA-1 和 提交消息
git log --oneline
  • -p选项

一个常用的选项是 -p,用来显示每次提交的内容差异(会输出diff差异)。 你也可以加上 -2 来仅显示最近两次提交:

git log -p -2
  • –stat选项
    查看每次提交的简略的统计信息
 git log --stat

比如,输出
这里写图片描述

  • –shortstat
    只显示 –stat 中最后的行数修改添加移除统计
git log --shortstat
  • –name-only
    仅在提交信息后显示已修改的文件清单。
git log --name-only
  • –name-status
    显示新增、修改、删除的文件清单。
git log --name-status
  • –abbrev-commit
    仅显示 SHA-1 的前几个字符(我这显示7个),而非所有的 40 个字符
git log --abbrev-commit
  • –graph
    显示 ASCII 图形表示的分支合并历史。
git log --graph
  • –pretty
    使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。
git log --pretty=

如用 format,可以定制要显示的记录格式(format 常用的选项 ):

$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 6 years ago : changed the version number
085bb3b - Scott Chacon, 6 years ago : removed unnecessary test
a11bef0 - Scott Chacon, 6 years ago : first commit
  • –since, –after
    仅显示指定时间之后的提交(since和after都可以)
git log --since=2.weeks #两周前的提交  这里用=或空格都可以
  • –until, –before
    仅显示指定时间之前的提交(until和before都可以)
git log --before 2016-10-01 
  • –author
    仅显示指定作者相关的提交。(作者指的是实际作出修改的人)
git log --author [stone]
  • –committer
    仅显示指定提交者相关的提交。(提交者指的是最后将此工作成果提交到仓库的人)
git log --committer [zsan]
  • –grep
    仅显示含指定关键字的提交
git log --oneline --grep "add"

如,输出:
这里写图片描述

  • -g选项
    这个命令会以标准日志的格式输出引用日志
git log -g

注:当要退出log视图,在冒号后,按q即可
查看当前分支的commit记录。在reset 到历史版本后,git log命令是看不到新的提交记录的

reflog

当你正在工作时,Git 会默默地记录每一次你改变 HEAD 时它的值。 每一次你提交或改变分支,引用日志都会被更新

git reflog

merge 和 rebase

conflict

bisect

比如有一个bug,不知道哪个历史提交引起的。这时就可以用bisect相关命令来查找。该命令采用二分法查找

  • 开始查找:
 git bisect start

使用git log,能看到提交记录(如要用到,可以只取前7位),
通过从提交记录检出成新分支或git reset方式,再运行调试,查看bug是否存在。
最好是重新拉一个新的项目仓库下来,专门来做这个调试工作。

  • 当确认bug存在时,标记找到问题:
git bisect good 33f4b9e #在33f4b9e提交记录中,有问题
  • 接着再找一个相对较早一些的提交记录(自己评估下),运行、调试。
    若存在bug,操作同上。若不存在,标记未找到问题:
git bisect bad 0a94086 #在0a94086提交记录中,未找到
  • 当至少有一个bad和一个good后,就会提示,大概还需要多少次就能定位到问题了:
Bisecting: 12 revisions left to test after this (roughly 4 steps)

大意:中间还有12个版本,大概最多还要4次测试,就能定位到问题了。

注:文末引用文章中,把没有问题的标记为good,有问题的才标记为bad。
这里的示例,是将有问题的标记为good,没有问题的标记为bad。

blame 责任查找

这个命令可以将文件中的每一行的作者、最新的变更提交和提交时间展示出来。

git blame [file_name]

这里写图片描述

diff 比较文件改动差异

  • 比较未暂存文件改动差异
    比如一个已add的文件,经过改动后,尚未再次add,输入命令:
git diff

如图:比较SpacePresenter 最新版本记录(-)和当前改动(+)后,文件间的差异
这里写图片描述

  • 比较已暂存文件改动差异
git diff --cached 
git diff --staged #功能同cached。 git1.6.1及更高版可用 

比较文件的前后差异。可以通过GUI工具来查看,show difference
Git Diff 的插件版本
可以使用 git difftool 命令来用 Araxis ,emerge 或 vimdiff 等软件输出 diff 分析结果。 使用 git difftool –tool-help 命令来看你的系统支持哪些 Git Diff 插件。

patch

如果项目,只有你一个人在维护。其他人想要commit、push是不行的。但他们clone下来项目,经过修改后,可以生成并发送一个patch给你,让你来合并代码。类似github的pull request
用到的命令:
git format-patch #生成patch文件
git apply xxx.patch #应用patch

remote远程仓库

如果是从远程仓库检出的项目,可以直接输入命令:

git remote

至少能看到类似 origin 的输出。origin是一般默认的远程仓库名。

  • -v选项
    会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL。
git remote -v

如,输出:

origin  https://github.com/googlesamples/android-architecture.git (fetch)
origin  https://github.com/googlesamples/android-architecture.git (push)

fetch,是客户端拉取最新信息的地址;push就是上传的地址。

  • add选项
    添加一个新的远程 Git 仓库,同时指定一个你可以轻松引用的简写:
git remote add [pb https://github.com/paulboone/ticgit]

如上,pb就是指定的远程仓库简写。
再用-v命令查看:

$ git remote -v
origin  https://github.com/schacon/ticgit (fetch)
origin  https://github.com/schacon/ticgit (push)
pb  https://github.com/paulboone/ticgit (fetch)
pb  https://github.com/paulboone/ticgit (push)

现在,就关联了两个远程仓库地址了。在fetch或push时,可以指定远程仓库名,如:

git push [pb master] # push到远程仓库pb上的master分支
git fetch [pb] # fetch远程仓库pb上所有新的信息
  • show选项
git remote show [origin]

会输出fetch、push的url,当前所在分支,不在本地的远程分支,哪些远程分支已经从服务器上移除了,还有pull和push时本地与远程分支的关联

  • rename选项
    重命名引用的远程仓库名字
git remote rename [pb paul]
  • rm选项
    删除远程仓库引用。
git remote rm [paul]  #rm 换成 remove也是可以的

其它


使用GitHub上的开源项目

当clone好项目后,可以建立自己的本地分支,进行调试等。
这样方便,以后合并项目某个分支,主分支单独更新等。

Git 与其他系统 - 迁移到 Git

详情:
git-scm.com/book/zh/v2/…

Reference


git-scm.com/book/zh/v2/ 《官方指南》
www.cnblogs.com/haiq/archiv… 《ignore语法》
www.cnblogs.com/wish123/p/3… 《diff命令详解》
gitbook.liuhui998.com/5_4.html 《查找问题的利器 - Git Bisect》
www.oschina.net/translate/1… 《10 个迅速提升你 Git 水平的提示》