初识Git

By | 2018年5月7日

目录:

  1. Git简介
  2. Git安装及使用

 

一、Git简介

Git 是一个开源的分布式版本控制系统,用以敏捷高效地处理任何或大或小的项目。

先来理解下 Git 工作区、暂存区和版本库的概念:

  1. 工作区:就是你在电脑里能看到的目录,持有实际文件。
  2. 暂存区:它像个缓存区域,临时保存你的改动。一般存放在 “.git” 目录下的index文件中,我们把暂存区有时也叫作索引(index)。
  3. 版本库:工作区有一个隐藏目录 .git,这个不算工作区,而是Git的版本库。

图中的 objects 标识的区域为 Git 的对象库,实际位于 “.git/objects” 目录下,里面包含了创建的各种对象及内容。

  • 当对工作区修改(或新增)的文件执行 “git add” 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。
  • 当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。

1352126739_7909

Git 的一般工作流程如下:

  1. 克隆 Git 资源作为工作目录;
  2. 在克隆的资源上添加或修改文件;
  3. 如果其他人修改了,你可以更新资源;
  4. 查看修改、提交修改;
  5. 在修改完成后,如果发现错误,可以撤回提交并再次修改后提交。

 

二、Git安装及使用

Git 目前支持在 Linux/Unix、Solaris、Mac和 Windows 多个平台上运行。具体参考Git安装配置教程。

我安装的是Windows版本,下载地址:http://msysgit.github.io/

安装完之后,在你希望存放仓库的文件夹下右击选择“Git Bash Here”

1.创建新仓库

执行 git init,就可以创建一个 Git 仓库了。

$ git init   #初始化

此时你可以看到在你的项目中生成了 .git 这个子目录, 这就是你的 Git 仓库,所有有关此项目的快照数据都存放在这里。

2.检出已有远程仓库

注意:如果已经有了远程仓库,那么不需要第一步的初始化工作,直接进行第二步。

使用 git clone 拷贝一个 Git 仓库到本地,使自己能够查看该项目,或者进行修改。

方法一:https模式

$ git config --global user.name 'your_user_name'            #Git为你的每一个提交都记录你的名字与电子邮箱地址,仅第一次需要

$ git config --global user.email 'your_email_address'

$ git clone <repo>     #<repo>中为已有project的http地址

方法二:ssh模式

$ ssh-keygen -t rsa -C "your_email_address" -b 4096       #首先生成key,“”中为你的邮箱地址,这个邮箱貌似随意

$ cat ~/.ssh/id_rsa.pub                  #查看public key,并在git上面导入这个key

$ git clone <repo>                    #<repo>中为已有project的ssh地址

方法三:git remote

$ git remote add origin <repo>        #添加远程仓库

配置用户信息、添加SSH key都是为了确保远程仓库的安全。比如别人将他的本地仓库关联到了我的远程库,关联不存在问题,但是想要推送内容是推不上去的,因为他的SSH Key公钥不在我的账户列表中。

这时,你的文件夹下会生成一个新目录,就是已有project的名字。这一步将会复制该项目的所有文件。

接着进入到该project文件夹下,此时默认在“master”分支下。

$ cd <projectname>     #projectname 为文件夹名

但是目前存在一个问题,如果远程仓库中除了“master”分支还有其他分支,此时你download下来的本地仓库是只包含“master”分支的,你还需要将远程仓库中的其它分支拉取到本地。

3.拉取远程分支并创建本地分支

3.1 列出分支

$ git branch        #没有参数时,git branch 会列出你在本地的分支

$ git branch -r     #查看远程仓库中的分支

3.2 拉取远程分支

方法一:

$ git fetch origin <远程分支名>:<本地分支名>            #在本地新建分支,但是不会自动切换到该本地分支,需要手动checkout

方法二:

$ git checkout -b <本地分支名> origin/<远程分支名>      #在本地新建分支,并自动切换到该本地分支

git fetch 与 git pull 的区别:

  1. git fetch:相当于是从远程获取最新版本到本地,不会自动merge;
  2. git pull:相当于是从远程获取最新版本并merge到本地,相当于git fetch 加 git merge。
$ git pull origin master

在实际使用中,git fetch更安全一些。因为在merge前,我们可以查看更新情况,然后再决定是否合并。

需要注意的是,这里拉取远程分支的命令,一次只能拉一个。如果你的远程仓库中有很多分支,而且你都需要拉取到本地的话,那么你需要一个一个地拉……

好,目前为止,基础工作算是完成了。接下来,你就可以做其它各种更新操作了。

 

其它简单操作的介绍如下:

1.添加新文件,修改文件

$ touch <filename>      #添加新文件

$ vim <filename>         #修改文件,按一下字母「i」进入插入模式,开始输入文字。输入完成后先按一下「ESC」键转到命令行模式,再输入「:wq」退出编辑

$ head -1 <filename>     #显示文件的第一行

$ cat <filename>         #向文件新增加一行

 2.查看项目状态,将内容添加到缓存区,取消缓存

$ git status            #查看项目的详细状态

$ git add <filename>    #将文件添加到缓存区
$ git add .
$ git add *             #"."和"*"表示将该分支下所有文件一起add,这三个命令根据需要进行选择

$ git status -s           #查看项目的当前状态,-s 参数表示简短的结果输出
?? hello.php              #表示还没有做任何推送
A  hello.php              #"A"表示这个文件已经添加到缓存区了
AM  hello.php             #"AM"表示这个文件在我们将它添加到缓存区后又有改动
M  hello.php              #"M"表示这个文件已经被修改,红色表示还未add,绿色才表示被add了
                          #如果这一行为空,表明已将缓存区内容添加到了仓库

$ git reset HEAD -- <filename>     #取消已缓存的内容,即在git add之后进行

 3.实际提交改动

将缓存区内容实际提交改动

$ git commit                 #将缓存区内容添加到仓库中

$ git commit -m '备注信息'    #将缓存区内容添加到仓库中并添加相应的注释
[dev fa439f4] update self_intro.txt by jyq
 1 file changed, 1 insertion(+)
 create mode 100644 self_intro.txt

$ git commit -a -m '备注信息'   #如果你觉得git add提交缓存的流程太过繁琐,Git也允许你用 -a 选项跳过这一步

-a 选项可将所有被修改或者已删除的且已经被git管理的文档提交到仓库中,-a 不会造成新文件被提交,只能修改。

现在,你的改动已经提交到了本地仓库的HEAD,但是还没到你的远端仓库。

4.推送改动

将改动提交到远端仓库

$ git push origin <branchname>      #<branchname>为你想要推送的任何分支
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 628 bytes | 69.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
remote:
remote: To create a merge request for dev_jyq, visit:
remote:   https://git.cmb.cloud/zzhou5/git_practice/merge_requests/new?merge_request%5Bsource_branch%5D=dev_jyq
remote:
To ssh://sshgit.cmb.cloud:1024/zzhou5/git_practice.git
 * [new branch]      dev_jyq -> dev_jyq

  5.删除文件

要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除,然后提交。

$ git rm <filename>

$ git rm -f <filename>        #如果删除之前修改过并且已经放到暂存区了,则必须要用强制删除选项-f

$ git rm --cached <filename>        #如果把文件从暂存区移除,但仍然希望保留在当前工作目录中

$ git rm –r *                 #进入某个目录中,执行此语句,会删除该目录下的所有文件和子目录

 6.移动文件

git mv 命令用于移动或重命名一个文件、目录、软连接。

$ git mv <filename1> <filename2>       #将filename1文件的名字改为filename2

 7.分支管理

使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。

分支将工作切分开来,从而让我们能够在不同上下文中做事,并来回切换。

在你创建仓库的时候,master 是“默认的”分支。在其他分支上进行开发,完成后再将它们合并到主分支上。

7.1 创建分支

$ git branch <branchname>          #建立分支

$ git push origin <branchname>     #除非你将分支推送到远端仓库,不然该分支就是 不为他人所见的

 7.2 切换分支

$ git checkout <branchname>            #切换分支
$ git checkout -b <branchname>         #建立分支并切换

本地分支名一般情况下与远程分支名相同,也可以不同。

7.3 删除分支

如果创建了新的分支,还没有将其push到远程仓库,下面一条删除语句就够了,因为只用删除本地仓库的分支即可:

$ git branch -d <branchname>

如果创建的新分支已经push到远程仓库了,那么删除分支需要以下几步:

$ git fetch origin <远程分支>:<本地分支>      #先把要删除的分支拉取下来

$ git branch -d <branchname>                      #删除分支,此时只是在本地仓库删除了分支,远程仓库中仍然存在

$ git branch                   #该语句只是查看本地仓库,所以执行后需要删除的分支不在了
$ git branch -r               #该语句查看远程仓库,所以执行后需要删除的分支仍然存在

$ git push origin -d <branchname>               #必须将更新推送到远程仓库

$ git branch -r              #此时结果中不再包含需要删除的分支了

7.4 合并分支

一旦某分支有了独立内容,你终究会希望将它合并回到你的主分支。

$ git merge <branchname>        #将<>中的分支合并到当前分支上来

 7.5 合并冲突

合并并不仅仅是简单的文件添加、移除的操作,Git 也会合并修改。

7.5.1 分支合并冲突

change_site 分支和 master 分支中本来都有相同的 test.txt 文件,然后两个分支分别对 test.txt 进行了不同的修改并且push到了远程仓库。现将 change_site 分支合并到 master 分支中,发生了冲突,示例如下:

$ git merge change_site
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.

$ cat test.txt 
<<<<<<< HEAD
runoob.com
新增加一行
=======
www.runoob.com
>>>>>>> change_site

手动修改冲突,打开 test.txt 文件,决定保留哪个分支对该文件所做的修改。

$ vim test.txt

$ cat test.txt 
www.runoob.com
新增加一行

$ git diff
diff --cc test.txt
index f84c2a4,bccb7c2..0000000
--- a/test.txt
+++ b/test.txt
@@@ -1,2 -1,1 +1,2 @@@
- runoob.com
+ www.runoob.com
 +新增加一行

然后要用 git add 告诉 Git 文件冲突已经解决,标记为合并成功,并提交结果。

$ git status -s
UU test.txt

$ git add test.txt 

$ git status -s
M  test.txt

$ git commit
[master 88afe0e] Merge branch 'change_site'

在合并改动之前,你可以使用如下命令预览差异:

$ git diff <source_branch> <target_branch>

7.5.2 同分支不同本地仓库修改冲突

当两个工作者对同一份文档进行修改并先后提交时,就会发生冲突。

$ git add .
$ git commit -m 'update self_intro.txt by jyq'

$ git push origin dev
Username for 'https://git.cmb.cloud': jyq
To https://git.cmb.cloud/zzhou5/git_practice.git
 ! [rejected]        dev -> dev (fetch first)
error: failed to push some refs to 'https://git.cmb.cloud/zzhou5/git_practice.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

$ git pull origin dev
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 2 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://git.cmb.cloud/zzhou5/git_practice
 * branch            dev        -> FETCH_HEAD
   2ac9e36..cadf492  dev        -> origin/dev
Auto-merging self_intro.txt
CONFLICT (content): Merge conflict in self_intro.txt
Automatic merge failed; fix conflicts and then commit the result.

$ vim self_intro.txt
$ git add self_intro.txt
$ git commit -m 'update the conflict in self_intro.txt'

$ git push origin dev
Username for 'https://git.cmb.cloud': jyq
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 616 bytes | 102.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
remote:
remote: To create a merge request for dev, visit:
remote:   https://git.cmb.cloud/zzhou5/git_practice/merge_requests/new?merge_request%5Bsource_branch%5D=dev
remote:
To https://git.cmb.cloud/zzhou5/git_practice.git
   cadf492..2b2efd6  dev -> dev

  8.查看提交历史

在使用 Git 提交了若干更新之后,又或者克隆了某个项目,想回顾下提交历史,我们可以使用 git log 命令查看。

$ git log

我们可以用 –oneline 选项来查看历史记录的简洁版本。

$ git log --oneline

还可以用 –graph 选项,查看历史中什么时候出现了分支、合并,更清楚明了地看到何时工作分叉、又何时归并。

$ git log --oneline --graph

也可以用 ‘–reverse’参数来逆向显示所有日志。

$ git log --reverse --oneline

如果只想查找指定用户的提交日志可以使用命令:git log –author

$ git log --author=<authorname> --oneline -5

如果你要指定日期,可以执行几个选项:–since 和 –before,但是你也可以用 –until 和 –after。

$ git log --oneline --before={3.weeks.ago} --after={2010-04-18} --no-merges     #看 Git 项目中三周前且在四月十八日之后的所有提交,--no-merges 选项用以隐藏合并提交

9.撤销——重置、签出、回滚

建议使用git命令行操作git而不是git的相关工具,因为git命令行可以让你知道每个命令做了什么,下一步要做什么。这样查看历史版本以及回滚的时候才会更加明晰。

9.1 重置——修复未提交文件中的错误

如果你现在的工作目录搞的一团乱麻,但是你还没有把它们提交,你可以通过下面的命令,让工作目录回到上次提交时的状态。这条命令会把你工作目录中所有未提交的内容清空(不包括未置于版本控制下的文件 untracked files)

$ git reset --hard HEAD

实际上,当执行 “git reset HEAD” 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。

9.2 签出

如果只需要恢复一个文件,以下命令把文件从HEAD中签出并且把它恢复成未修改时的样子。

$ git checkout -- <filename>

实际上,当执行 “git checkout .” 或者 “git checkout — <file>” 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。

 9.3 回滚——修复已提交文件中的错误

创建一个新的提交(commit),在新的提交里撤消老的提交所作的修改。只要把出错的提交(commit)的名字作为参数传给 git revert 命令就可以了。

$ git revert <commit_name>

注意:revert 之后还需要 add-commit-push 等操作。

git reset 和 git revert 的区别:

  1. git reset 是直接删除指定的commit,把HEAD往回移动了一下;
  2. git revert是一次新的特殊的commit,HEAD继续前进,本质和普通的 add commit 操作一样,仅仅是commit的内容比较特殊:提交的内容是与前面普通commit的反操作。比如前面普通commit是增加一行a,那么revert内容就是删除一行a。

举个例子:

假设按时间顺序依次有commit1, commit2, commit3,commit4,对应有version1, version2, version3, version4四个状态,现在需要撤销 commit4。

  1. git reset 是回到了version3;
  2. git revert 是新建一个提交 commit5,得到 version5,不过 version5 和 version3 的内容是一样的。

 

参考文献:

  1. Git 安装配置
  2. git简明指南
  3. git拉取远程分支并创建本地分支和Git中从远程的分支获取最新的版本到本地
  4. 删除本地git的远程分支和远程删除git服务器的分支
  5. Git的撤消操作 – 重置, 签出 和 撤消
  6. git revert实战
  7. git revert的使用

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注