Git

尽管自己一直与Github有所接触,比如用Github交个作业啦,Fork一个项目啦……但是对于git命令的使用还知之甚少。于是我决定对自——

Git,个人博客的开篇决定就是你了!

What’s Git

Git is a distributed version control system developed by Junio Hamano and Linus Torvalds.

Quoting Linus: “I’m an egotistical bastard, and I name all my projects after myself. First ‘Linux’, now ‘Git’”.

(‘git’ is British slang for “pig headed, think they are always correct, argumentative”).

——扒自git.wiki.kernel.org

简而言之,Git就是Linux之父Linus开发的一个分布式版本控制工具。至于Git名称的来源嘛,呵呵,它来自于英国俚语,意思是“蠢货”、“饭桶”,不太友好。

版本控制

所谓“版本控制”的概念,来源于软件工程。一个Project在开发过程中无可避免进行多次迭代,对于每一代,软件工程师会赋予其相应的版本号,还会附上相应的文档,以记录当前迭代所进行的更改。而对于大型工程,在多人协作的情况下版本控制的问题就更加critical。若是管理者手动进行版本控制,则显得异常恐怖。在这种情况下,版本控制软件就应运而生了。

差分编码

版本控制的常用算法是差分编码:对于一个基准版本 Baseline,后续版本在存储时只需要保存与基准版本之间的差异就可以了。差异储存在称为 deltadiff 的不连续档案中。由于改变通常很小(平均占全部大小的2%),差分编码能大幅减少资料的重复。一连串独特的 delta 档案在空间上要比未编码的相等档案有效率多。

分布式版本控制

传统的版本控制软件采用的是集中模式的版本控制:不同版本的文件都必须存放在一个中央服务器中,这样就要求一个服务器来集中托管版本库。对于Git这种分布式版本控制系统来说,每一个用户的电脑上都是一个完整的版本库。因而,由服务器故障引发的安全性问题在Git上不可能出现。

多插一句,Git自从诞生之日起,就肩负起了史上最复杂的开源项目Linux Kernel的版本控制工作。它的优越性由此可见一斑。更何况如今Github风靡一时,不妨也来使用Git驱动的Github托管你的代码吧!

Installation

在系统自带的terminal中敲下git并回车,如果系统显示如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
➜  ~ git     
usage: git [--version] [--help] [-C <path>] [-c name=value]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
<command> [<args>]

These are common Git commands used in various situations:

start a working area (see also: git help tutorial)
clone Clone a repository into a new directory
init Create an empty Git repository or reinitialize an existing one
...
...
...
➜ ~

则表明你的系统上已经装有Git,可以跳过安装步骤了。

否则需要根据系统的不同,选择对应方法安装Git:

Win

Git - Downloading Package下载并安装Git For Windows,然后在系统PATH变量中加入Git的安装目录即可。

macOS

安装Xcode Command Line Tools,这是Apple官方的软件开发工具,包含git, llvm, make等等常用命令行软件开发程序。

或者,安装Homebrew,这是一款macOS平台上流行的软件包管理工具,“使用Homebrew可以安装 Apple没有预装但你需要的东西”。

Linux

使用Linux下传统的安装工具,如apt,yum等,即可安装Git。

git init - demo

值得注意的是,在开始工作前,首先使用mkdir命令创建一个工作目录,并且进入这个目录:

1
2
mkdir Helloworld
cd Helloworld

然后键入git status命令,会看到如下提示:

1
2
➜  Helloworld git status
fatal: Not a git repository (or any of the parent directories): .git

表明现在Helloworld目录还并不是一个git repository,我们需要使用git init命令来初始化这个目录:

1
2
3
➜  Helloworld git init
Initialized empty Git repository in /Users/xiangdongwei/Helloworld/.git/
➜ Helloworld git:(master)

我们发现shell的prompt也发生了变化,说明初始化成功,接下来我们就可以正式使用Git啦!

Basics

lifecycle

Git 有三种状态,你的文件可能处于其中之一:已提交 committed、已修改 modified 和已暂存 staged 。 已提交表示数据已经安全的保存在本地数据库中。 已修改表示修改了文件,但还没保存到数据库中。 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。而未跟踪 untracted 的文件并未被纳入到Git版本系统之中。

基本的 Git 工作流程如下:

  1. 在工作目录中修改文件。
  2. 暂存文件,通过git add将文件的快照放入暂存区域,即暂存更新。
  3. 提交更新,通过git commit找到暂存区域的文件,将快照永久性存储到 Git 仓库目录,即提交更新。

对于git init初始化的目录,所有的文件都默认保持在 Untracked 状态。另外,可通过git status来查看当前工作目录下的文件状态。

注意:git commit命令会默认打开vim,此时在非注释行键入提交信息,保存退出即可提交更新。

git rm

git rm --cached可以使文件恢复 Untracked 状态,git rm会直接从目录中删除文件。

而在Git中rm不能真正删除文件(Git会视为未提交的修改)。

git mv

使用git mv file_from file_to,可以对文件进行改名,当然传统的mv命令也可以使用。

git diff

通过git diff可以查看修改之后还没有暂存起来的变化内容,git diff --cached可以查看查看已暂存的将要添加到下次提交里的内容。

撤销操作

git commit --amend:重新进行提交,覆盖上次的提交结果。

git reset HEAD <file>:取消暂存的文件,其中HEAD^/HEAD^^表示上一个和上上个状态,更早版本可以使用HEAD~n来表示。如果参数是HEAD则表示暂存区的修改回退到工作区中。

git checkout --<file>:撤销对文件的修改(即恢复到最近一次commit或者add的状态)慎用!

小结

来自廖雪峰的Git教程

场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout --file

场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。

场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,使用get reset HEAD^、`get reset –hard commit_id等命令,不过前提是没有推送到远程库。

git branch

使用git branch branch_name来创建分支,git checkout branch_name来改变当前分支(注意git checkout还有撤销修改的功能!)git merge branch_name将某个分支合并到当前分支上。

此外,git checkout -b branch_name则将branchcheckout两条命令合一;git checkout -d branch_name代表删除分支。

Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁都异常高效。 创建一个新分支就相当于往一个文件中写入 41 个字节(40 个字符和 1 个换行符)。branch种种高效的特性使得Git鼓励开发人员频繁地创建和使用分支!

远程仓库

使用git clone [url]克隆现有的仓库,而git remote add <shortname> <url>可将本地仓库与已有远程仓库关联起来,按照惯例远程仓库通常命名为origin,git clone默认关联的远程仓库代号也是origin。

git push origin branch_name将本地分支的修改推送到远程仓库origin上,git pull branch_name origin将本地分支抓取远程origin分支并且自动尝试合并。git fetch [remote_name]可以拉取远程仓库上的所有分支。

本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致;建立本地分支和远程分支的关联,使用git branch --set-upstream branch-name origin/branch-name

git remote附加命令还有:

command_name 描述
git remote rename 重命名远程仓库的代号
git remote rm 移除与远程仓库的关联
git remote show shortname 显示某一远程分支的相关信息
git remote [-v] 显示所有关联的远程分支

Exotic

git log

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

命令行参数 含义 命令行参数 含义
-p 显示每次提交的差异 -(n) 显示最近提交的n次修改的内容
–stat 查看每次提交的简要统计信息 –pretty=oneline/full/format 定制化显示记录格式
–graph 添加一些ASCII字符串来展示分支、合并历史 –shortstat 只显示 –stat 中最后的行数修改添加移除统计
–name-only 仅在提交信息后显示已修改的文件清单 –name-status 显示新增、修改、删除的文件清单
–abbrev-commit 仅显示 SHA-1 的前几个字符 –relative-date 使用较短的相对时间显示
–since/until 按照时间筛选 –author 按照作者筛选
–grep 以正则匹配来筛选 -S 仅显示添加或移除了某个关键字的提交

git tag

git tag <tagname>为当前分支打标签,git show <tagname>显示相应标签的提交信息, git tag -d <tagname>则可以删除相应标签。标签在Git中非常实用,可以通过标签创见新的分支。

因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。 在创建完标签后必须显式地推送标签到共享服务器上:使用命令git push origin <tagname>

git stash

假设说开发dev分支过程中突然需要修复一个bug,可以使用git stash命令来保存未完成的现场,然后工作区会变为last commit的状态。此时,就可以安心从master分支来修复bug了。完成之后,如何恢复之前stash的未完成状态呢?

首先切换回dev分支,使用git stash list查看之前stash的内容,然后有两种选择:

  1. git stash pop就像弹栈,恢复并删除上次stash的内容
  2. git stash apply stash@{n}可以指定恢复某个stash,git stash drop可以删除相应stash

.gitignore

通过编辑这个文件,可以让Git“无视”工作目录下的某些文件。

实例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# no .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 the build/ directory
build/

# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt

# ignore all .pdf files in the doc/ directory
doc/**/*.pdf

配置Git

git config --global config_name value是设置全局配置的通法,git config则只对当前目录有效。通常需要设置的是user.nameuser.emailcore.editor等变量。可以通过git config --list查看所有配置。

git config alias.alias_name 'original_command'可以为变量设置别名。