万物科技 学,以致用

Git入门及基本原理

2017-10-25
Geng

git 作为一个常用的软件, 入门总是必须的, 另外了解下原理, 也挺好.

参考文献:

  • http://gitimmersion.com/
  • https://git-scm.com/blog
  • http://marklodato.github.io/visual-git-guide/index-zh-cn.html

安装

不会的话来这里

功能

简单说, 可以做版本管理

体验

如果使用过的话, 这部分可以忽略. 没有用过的话, 体验一下** git** 到底干什么.

首次使用

git config --global user.name "Your Name"
git config --global user.email 

初始化

这步可以让一个文件夹可以使用 git.

进入一个你准备要使用git的文件夹,比如一个空文件夹,然后终端输入:

git init

新建任意一个文件

新建一个文件:”hello.py”, 内容如下:

print("Hello, World!")

暂存

git add hello.py
git commit -m "First Commit"

查看状态

git status

此时终端可以看到一堆信息

改变文档之后,再查看状态

随便修改一下文档, 运行:

git status

暂存之后, 再查看状态

git add hello.py
git status

查看历史纪录

git log
git log --pretty=oneline
git log –all
git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short

返回历史状态

这个命令是时空穿梭机, 可以让你到达以前提交的任意版本.

git checkout <hash>
就是 `log` 看到的那一串字符, 使用前六位即可.

到达最新版本

也可以从过去到现在:

git checkout master

别名

刚才的 log 命令太复杂? 没关系, 我们使用别名解决. 当然别名也可以设置其他命令的别名.

在你的 “$HOME” 目录建立 “.gitconfig”:

[alias]
  co = checkout
  ci = commit
  st = status
  br = branch
  hist = log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
  type = cat-file -t
  dump = cat-file -p

标签

别名解决了命令太长的问题, 但是前面的例子,使用checkout hash的形式,hash不好记忆,不利于追溯。

使用标签解决

打标签

当前版本下, 运行:

git tag v1

给v1之前那个版本也打一个标签”v1-beta”:

git checkout v1^
git tag v1-beta

试试(结合hist):

git checkout v1-beta
git checkout v1

列出所有标签

git tag

删除标签

git tag –d tag1

暂存和提交

到这里, 你已经会用 git 基本的功能了, 可以开始工作了. 这里简单介绍一下暂存和提交.

上面命令可以看出, 暂存用 add, 提交用 commit.

分离暂存和提交的好处:不需要做版本管理就先不做,需要的时候再做。

假设你修改了三个文件 (a.pyb.py, 和 c.py)。现在你想提交所有变化,但是你想把 a.pyb.py 作为一个提交,而 c.py 和其他两个文件关系不大,你要分开提交。 

git add a.rb
git add b.rb
git commit -m "Changes for a and b"

git add c.rb
git commit -m "Unrelated change to c"

继续修改刚才任意文件, 暂存然后提交:

git add .
git commit -m "other change"

git 基本原理

下面的部分, 很多是 git 的原理, 没有兴趣可以不管, 但是知道了肯定用 git 会更舒服.

建立git仓库

有两种获取 Git 项目仓库的方法。第一种是在现存的目录下,通过导入所有文件来创建新的 Git 仓库。第二种是从已有的 Git 仓库克隆出一个新的镜像仓库来.

本地目录建立

git init

初始化后,在当前目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。

记录更新到仓库

工作目录下面的所有文件都不外乎这两种状态:已跟踪或未跟踪

已跟踪的文件是指本来就被纳入版本控制管理的文件,在上次快照中有它们的记录,工作一段时间后,它们的状态可能是未更新,已修改或者已放入暂存区。

所有其他文件都属于未跟踪文件。它们既没有上次更新时的快照,也不在当前的暂存区域。初次克隆某个仓库时,工作目录中的所有文件都属于已跟踪文件,且状态为未修改。 在编辑过某些文件之后,Git 将这些文件标为已修改。我们逐步把这些修改过的文件放到暂存区域,直到最后一次性提交所有这些暂存起来的文件,如此重复。

Git 内都只有三种状态

已提交(committed),已修改(modified)和已暂存(staged)。

  • 已提交表示该文件已经被安全地保存在本地数据库中了
  • 已修改表示修改了某个文件,但还没有提交保存
  • 已暂存表示把已修改的文件放在下次提交时要保存的清单中

对照前面提到的 add' 和 'commit 命令, 我们结合上图可以看到这两个命令的作用.

还有一个问题, 如果我想移除文件呢?

移除文件

要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。

可以用 git rm 命令完成此项工作,并连带从工作目录中删除指定的文件(相当于执行过rm命令),这样以后就不会出现在未跟踪文件清单中了。

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

On branch 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:    grit.gemspec
no changes added to commit (use "git add" and/or "git commit -a")

需要再次运行

git add .

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

$ git rm my_file.txt
git status


On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
deleted:    grit.gemspec

最后提交的时候,该文件就不再纳入版本管理了。如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f( 即 force 的首字母),以防误删除文件后丢失修改的内容。

另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。换句话说,仅是从跟踪清单中删除。比如一些大型日志文件或者一堆 .a 编译文件,不小心纳入仓库后,要移除跟踪但不删除文件,以便稍后在 “.gitignore” 文件中补上,用 --cached 选项即可:

git rm --cached readme.txt

Git 进阶

基本原理就这么多, 然后有空再了解下高级点的东西.

Git几个区域

Head 头:最后一次提交的快照 Stage(index)暂存区:下次提交的快照 工作目录:当前工作区

工作步骤详解

新建一个文件夹,并有一个文件。这个是此文件的 v1 版本(蓝色)。运行 git init,建立一个 git 仓库,head 指向空的分支

此时,只有工作目录有东西,然后使用 git add 将文件放入暂存区

然后通过 git commit 把文件从暂存区提交到仓库,并且 head 指向这个提交的版本。

此时,三个区域内容一样,运行 git status,你将会发现无变化

然后,我们想改变那个文件并且提交。首先,我们改变文件内容为 v2。运行 git status 会有红字提示 “changed but not updated”。

然后 git add 将v2暂存。

这个时候,运行 git status,显示 ‘Changes to be Committed’。因为现在暂存区和 head 不同,也就是预计下次提交和上次提交内容不同。最后,git commit 提交

常用命令图解

基本单步操作

  • git add files 把当前文件放入暂存区域。
  • git commit 给暂存区域生成快照并提交。
  • git reset -- files 用来撤销最后一次 git add files ,你也可以用 git reset 撤销所有暂存区域文件。
  • git checkout -- files 把文件从暂存区域复制到工作目录,用来丢弃本地修改。

(– 什么意思) Suppose I have a file named path/to/file.txt in my Git repository, and I want to revert changes on it. git checkout path/to/file.txt Now suppose that the file is named master… git checkout master Whoops! That changed branches instead. The – separates the tree you want to check out from the files you want to check out. git checkout – master It also helps us if some freako added a file named -f to our repository: git checkout -f # wrong git checkout – -f # right (http://stackoverflow.com/questions/13321458/meaning-of-git-checkout-double-dashes/28883539)

跳跃操作

跳过暂存区,历史版本和工作目录直接交互

  • git commit -a 相当于运行 git add 把所有当前目录下的文件加入暂存区域再运行 git commit

  • git commit files 进行一次包含最后一次提交加工作目录中files文件快照的提交。并且文件被添加到暂存区域:

git add files
git commit -m “…”
  • git checkout HEAD -- files 文件回滚到最后一次提交。

恢复以前版本

Reset

第一步,移动 HEAD

reset 第一个功能就是移动 HEAD 的指向位置。运行git reset 9e5e6a4 会将指针指向 9e5e6a4. 不管哪种形式的 git reset,这个都是第一步。如果使用 --soft,那这个也是唯一的一步。

注意:使用reset,HEAD永远指向master branch

这一步其实取消了上次的提交。当你再次 git commit 时,git 新建一个新的提交,并由 HEAD 指向。这个操作没有修改暂存区和工作目录,你现在可以做一些修改等操作然后commit,基本上就是 git commit --amend 的工作。

第二步,更新暂存区

然后想要将暂存区与 HEAD 内容同步,那就需要将 HEAD 指向的内容复制到暂存区。默认 —mixed 选项。相当于回滚了 git addgit commit

--mix 也是默认操作。

第三步,更新工作区域

使用 —hard 选项。放弃了所有的 git commitgit add 和文件修改。不要轻易开启 Hard 模式 。

git checkout 及其与 reset 比较

checkout

当不指定文件名,而是给出一个(本地)分支时,那么 HEAD 标识会移动到那个分支(也就是说,我们“切换”到那个分支了),然后暂存区域和工作目录中的内容会和HEAD对应的提交节点一致。新提交节点(下图中的 a47c3)中的所有文件都会被复制 (到暂存区域和工作目录中)

注意:checkout 不移动 master ,只移动 HEAD

Reset

reset 命令把当前分支指向另一个位置,并且有选择的变动工作目录和索引。也用来在从历史仓库中复制文件到索引,而不动工作目录。

如果不给选项,那么当前分支指向到那个提交。如果用 --hard 选项,那么工作目录也更新,如果用 --soft 选项,那么都不变。

Reset文件

如果 reset 一个文件,情况又不一样了。回忆 reset 步骤:

  1. 移动HEAD(如果—soft 就终止在这里)。
  2. 修改暂存区为HEAD指向内容(如果—hard模式还要修改工作路径)。

但是如果是重置一个文件呢?你能移动 HEAD 吗?那不是就整个更换到另一个提交版本了吗?你不能让指针只指向其中提交的一部分吧?

那么,假设运行 git reset file.txt,指定一个 SHA 或着分支,那么你将跳过第一步,直接执行第二步。

  1. 移动HEAD(如果—soft 就终止在这里)。
  2. 修改暂存区为HEAD指向内容(如果—hard模式还要修改工作路径)。

其实,这就是把 HEAD 指向的 file.txt 复制到暂存区。

那么这到底做了什么?其实就是取消暂存。那暂存是那个命令?add filereset file 做了正好相反的事情。

那我想将以前某个版本的文件 reset 呢?

如果不给选项,那么当前分支指向到那个提交。如果用 --hard 选项,那么工作目录也更新,如果用 --soft 选项,那么都不变。

如果没有给出提交点的版本号,那么默认用 HEAD。这样,分支指向不变,但是索引会回滚到最后一次提交,如果用 --hard 选项,工作目录也同样。

如果给了文件名(或者 -p 选项), 那么工作效果和带文件名的 git checkout 差不多,但是工作目录不更新。

区别与联系

git checkout/reset hash

是否移动分支

git checkout/reset – file

二者类似,都不会移动 HEAD,会将库里的文件复制到暂存区和工作目录。

如果运行 git checkout/reset – file,感觉起来就像运行类似 git reset --hard [branch] file,不过 reset – hard file 并不能运行。

恢复版本的几种情况

修改最后一次提交信息或者增删暂存文件!

有时候我们提交完了才发现漏掉了几个文件没有加,或者提交信息写错了。想要撤消刚才的提交操作,可以使用 --amend 选项重新提交:

git commit --amend

此命令将使用当前的暂存区域快照提交。如果刚才提交完没有作任何改动,直接运行此命令的话,将会重新编辑提交说明,但将要提交的文件快照和之前的一样。

  比如增加文件或者修改: 如果刚才提交时忘了暂存某些修改,可以先补上暂存操作,然后再运行 --amend 提交:

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

上面的三条命令最终只是产生一个提交,第二个提交命令修正了第一个的提交内容。

修改完文件,发现修改有问题,需要返回修改以前的状态(add之前)

修改文件,然后查看状态

git status

分析: 修改文件,但是没有 add:当前目录变了,暂存区和库没有变。那么只要从暂存区取出文件,放入工作目录,就行了

返回修改之前状态

git checkout hello.py
git status
cat hello.py

1.1.1 修改完文件且add,发现修改有问题,需要返回修改以前的状态(commit之前)

修改文件,执行 git add, 使用 git status 分析

从历史记录中找,但是要告诉git返回到哪个位置

返回之前状态

git reset HEAD hello.py

将 HEAD 的 “helo.py” 内容复制到暂存区

git checkout hello.py
git status

暂存区复制到工作目录

分支

建立分支

git checkout -b greet
git status

回到主分支

git checkout master

合并分支

merge 命令把不同分支合并起来。合并前,索引必须和当前提交相同。如果另一个分支是当前提交的祖父节点,那么合并命令将什么也不做。 另一种情况是如果当前提交是另一个分支的祖父节点,就导致fast-forward合并。指向只是简单的移动,并生成一个新的提交。

总结

差不多从基础到原理都有了, 但是内容不是很全, 不过工作基本够了, 查文档基本就能搞定了


Comments

你可以请我喝喝茶,聊聊天,鼓励我

Wechat Pay
wechat

Thanks!