15

Git高级使用教程

 3 years ago
source link: https://segmentfault.com/a/1190000037597769
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Git 与 SVN 区别

Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持。

GIT不仅仅是个版本控制系统,它也是个内容管理系统(CMS),工作管理系统等。如果你是一个具有使用SVN背景的人,你需要做一定的思想转换,来适应GIT提供的一些概念和特征。Git 与 SVN 区别点:

1、GIT是分布式的,SVN不是:这是GIT和其它非分布式的版本控制系统,例如SVN,CVS等,最核心的区别。

2、GIT把内容按元数据方式存储,而SVN是按文件:所有的资源控制系统都是把文件的元信息隐藏在一个类似.svn,.cvs等的文件夹里。

3、GIT分支和SVN的分支不同:分支在SVN中一点不特别,就是版本库中的另外的一个目录。

4、GIT没有一个全局的版本号,而SVN有:目前为止这是跟SVN相比GIT缺少的最大的一个特征。

5、GIT的内容完整性要优于SVN:GIT的内容存储使用的是SHA-1哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。

Git工作原理

Git工作流程可以分一下步骤:

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

QvAN3mJ.jpg!mobile

Git 工作区、暂存区和版本库:

  • 工作区:就是你在电脑里能看到的目录。
  • 暂存区:英文叫stage, 或index。一般存放在"git目录"下的index文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。
  • 版本库:工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。

nIRrueZ.jpg!mobile

常见操作

全局配置用户信息

git config --global user.name "smyhvae"

git config --global user.email "[email protected]"

Git 创建仓库

git init repository

如果当前目录下有几个文件想要纳入版本控制,需要先用 git add 命令告诉 Git 开始对这些文件进行跟踪,然后提交:

$ git add *.c
$ git add README
$ git commit -m 'initial project version'

从现有仓库克隆

$ git clone git://github.com/test/test.git

Git基本操作命令

git init

创建项目命令,在目录中创建新的 Git 仓库。 你可以在任何时候、任何目录中这么做,完全是本地化的。在目录中执行 git init,就可以创建一个 Git 仓库。

$ mkdir test
$ cd test
$ git init

git clone

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

$ git clone git://github.com/test/test.git

git push

其作用是将本地分支的更新推送到远程主机

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

git pull

其作用是将远程主机更新到本地分支

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

git add

git add 命令可将该文件添加到缓存,如我们添加以下两个文件:

$ touch README
$ touch hello.php
$ ls
README      hello.php
$ git status -s
?? README
?? hello.php
$

git status

命令用于查看项目的当前状态

$ git add README hello.php 

$ git status -s
A  README
A  hello.php
$

git diff

git diff 命令显示已写入缓存与已修改但尚未写入缓存的改动的区别。git diff 有两个主要的应用场景:

尚未缓存的改动:git diff

查看已缓存的改动: git diff --cached

查看已缓存的与未缓存的所有改动:git diff HEAD

显示摘要而非整个 diff:git diff --stat

$ git status -s
A  README
AM hello.php
$ git diff
diff --git a/hello.php b/hello.php

git commit

使用 git add 命令将想要快照的内容写入了缓存, 而执行 git commit 记录缓存区的快照。Git 为你的每一个提交都记录你的名字与电子邮箱地址,所以第一步需要配置用户名和邮箱地址。

$ git config --global user.name 'admin'
$ git config --global user.email [email protected]

$ git add hello.php
$ git status -s
A  README
A  hello.php
$ git commit -m 'test comment from test.cn'
[master (root-commit) 85fc7e7] test comment from test.cn
 2 files changed, 4 insertions(+)
 create mode 100644 README
 create mode 100644 hello.php

现在我们已经记录了快照。如果我们再执行 git status:

$ git status
# On branch master
nothing to commit (working directory clean)

git log

查看历史提交记录

$ git log
commit 88afe0e02adcdfea6844bb627de97da21eb10af1
Merge: 14b4dca d7e7346
Author: admin
Date:   Sun Mar 1 15:03:42 2020 +0800

    Merge branch 'change_site'
    
    Conflicts:
        test.txt

commit 14b4dcadbdc847207651d5a9fae0d315057f346e
Author: admin
Date:   Sun Mar 1 14:53:15 2015 +0800

git tag

可以使用 git tag 给打上标签

git reset HEAD

命令用于取消缓存已缓存的内容。

$ git status -s
 M README
 M hello.php
$ git add .
$ git status -s
M  README
M  hello.pp
$ git reset HEAD -- hello.php 
Unstaged changes after reset:
M hello.php
$ git status -s
M  README
 M hello.php

git rm将文件从缓存区中移除。

$ git rm hello.php 
rm 'hello.php'
$ ls
README

git mv命令做得所有事情就是 git rm --cached, 重命名磁盘上的文件,然后再执行 git add 把新文件添加到缓存区。因此,虽然有 git mv 命令,但它有点多余 。

R3MjUzr.jpg!mobile

分支的合并

场景:基于master分支的代码,开发一个新的特性

如果你直接在master分支上开发这个新特性,是不好的,万一你在开发 特性1 的时候,领导突然又要叫你去开发 特性2 ,就不好处理了。难道开发的两个特性都提交到master?一会儿提交特性1的commit,一会儿提交特性2的commit?这会导致commit记录很混乱。

所以,我给你的建议做法是:给每个特性都单独建一个的新的分支。

比如说,我专门给 特性1 建一个分支 feature_item_recommend 。具体做法如下:

(1)基于master分支,创建一个新的分支,起名为 feature_item_recommend

$ git checkout -b feature_item_recommend

Switched to a new branch 'feature_item_recommend'

上面这行命令,相当于:

$ git branch feature_item_recommend    // 创建新的分支

$ git checkout feature_item_recommend  //切换到新的分支

(2)在新的分支 feature_item_recommend 上,完成开发工作,并 commit 、push。

(3)将分支 feature_item_recommend 上的开发进度 合并 到master分支:

$ git checkout master  //切换到master分支

$ git merge feature_item_recommend    //将分支 feature_item_recommend 的开发进度合并到 master 分支

合并之后, master 分支和 feature_item_recommend 分支会指向同一个位置。

(3)删除分支 feature_item_recommend

既然 特性1 开发完了,也放心地提交到master了,那我们就可以将这个分支删除了。

git branch -d feature_item_recommend

注意,我们当前是处于 master 分支的位置,来删除 feature_item_recommend 分支。如果当前是处于 feature_item_recommend 分支,是没办法删除它自己的。

同理,当我转身去开发 特性2 的时候,也是采用同样的步骤。

合并分支时,如果存在分叉

Fve26nN.png!mobile

比如说上面这张图中,最早的时候,master分支是位于 C2 节点。我基于 C2 节点,new出一个新的分支 iss53 ,我在 iss53 上提交了好几个commit。

现在,我准备把 iss53 上的几个commit合并到master上,此时发现,master分支已经前进到C4了。那该怎么合并呢?

合并的命令仍然是:

$ git checkout master

$ git merge iss53

解释:

这次合并的实现,并不同于简单的并入方式。这一次,我的开发历史是从更早的地方开始分叉的。

由于当前 master 分支所指向的commit (C4)并非想要并入分支(iss53)的直接祖先,Git 不得不进行一些处理。就此例而言,Git 会用两个分支的末端(C4 和C5)和它们的共同祖先(C2)进行一次简单的三方合并计算。

Git 没有简单地把分支指针右移,而是对三方合并的结果作一新的快照,并自动创建一个指向它的commit(C6)(如下图所示)。我们把这个特殊的commit 称作合并提交(mergecommit),因为它的祖先不止一个。

值得一提的是Git 可以自己裁决哪个共同祖先才是最佳合并基础;这和CVS 或Subversion(1.5 以后的版本)不同,它们需要开发者手工指定合并基础。所以此特性让Git 的合并操作比其他系统都要简单不少。

iy26rmr.png!mobile

解决合并时发生的冲突

YR7JVzi.png!mobile

如果 feature1和feature2修改的是同一个文件中 代码的同一个位置 ,那么,把feature1合并到feature2时,就会产生冲突。这个冲突需要人工解决。步骤如下:

(1)手动修改文件:手动修改冲突的那个文件,决定到底要用哪个分支的代码。

(2)git add:解决好冲突后,输入 git status ,会提示 Unmerged paths 。这个时候,输入 git add 即可,表示: 修改冲突成功,加入暂存区

(3)git commit 提交。

然后,我们可以继续把 feature1 分支合并到 master分支,最后删除feature1、feature2。

注意:两个分支的同一个文件的不同地方合并时,git会自动合并,不会产生冲突。

比如分支feture1对index.html原来的第二行之前加入了一段代码。

分支feature2对index.html在原来的最后一行的后面加入了一段代码。

这个时候在对两个分支合并,git不会产生冲突,因为两个分支是修改同一文件的不同位置。

git自动合并成功。不管是git自动合并成功,还是在人工解决冲突下合并成功,提交之前,都要对代码进行测试。

日常操作积累

修改密码(曲线救国)

网上查了很久,没找到答案。最终,在cld童鞋的提示下,采取如下方式进行曲线救国。

# 设置当前仓库的用户名为空
git config  user.name ""

然后,当我们再输入 git pull 等命令行时,就会被要求重新输入 新的 账号密码。此时,密码就可以修改成功了。最后,我们还要输入如下命令,还原当前仓库的用户名:

git config user.name "smyhvae"

修改已经push的某次commit的作者信息

已经push的记录,如果要修改作者信息的话,只能 通过--force命令。

branch1 的某个 commit1 合并到 branch2 当中

切换到branch2中,然后执行如下命令:

git cherry-pick commit1

20200118-修改GitHub已提交的用户名和邮箱

在执行 ./email.sh 后,如果出现 permission denied 的错误,可以先执行 chmod 777 email.sh ,修改文件的权限。

20200520-将Git 项目迁移到另一个仓库

我们假设旧仓库的项目名称叫 old-repository ,新仓库的项目名称叫 new-repository 。操作如下:

(1)创建旧仓库的裸克隆:

git clone --bare https://github.com/exampleuser/old-repository.git

执行上述命令后,会在本地生成一个名叫 old-repository.git 的文件夹。

(2)迁移到新仓库:

cd old-repository.git

git push --mirror https://github.com/exampleuser/new-repository.git

这样的话,项目就已经迁移到新仓库了。

注意,我们 不需要 手动新建一个空的新仓库,当我们执行上述命令之后,新仓库就已经自动创建好了。

参考链接:

Git 服务器搭建

我们将以 Centos 为例搭建 Git 服务器。

1、安装Git

$ yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-devel
$ yum install git

创建一个git用户组和用户,用来运行git服务:

$ groupadd git
$ adduser git -g git

2、创建证书登录

收集所有需要登录的用户的公钥,公钥位于id_rsa.pub文件中,把我们的公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。如果没有该文件创建它:

$ cd /home/git/
$ mkdir .ssh
$ chmod 700 .ssh
$ touch .ssh/authorized_keys
$ chmod 600 .ssh/authorized_keys

3、初始化Git仓库

首先我们选定一个目录作为Git仓库,假定是/home/gitrepo/hello.git,在/home/gitrepo目录下输入命令:

$ cd /home
$ mkdir gitrepo
$ chown git:git gitrepo/
$ cd gitrepo

$ git init --bare hello.git
Initialized empty Git repository in /home/gitrepo/hello.git/

以上命令Git创建一个空仓库,服务器上的Git仓库通常都以.git结尾。然后,把仓库所属用户改为git:

$ chown -R git:git w3cschoolcn.git

4、克隆仓库

$ git clone [email protected]:/home/gitrepo/w3cschoolcn.git
Cloning into 'w3cschoolcn'...
warning: You appear to have cloned an empty repository.
Checking connectivity... done.

192.168.45.4 为 Git 所在服务器 ip ,你需要将其修改为你自己的 Git 服务 ip。这样我们的 Git 服务器安装就完成了,接下来我们可以禁用 git 用户通过shell登录,可以通过编辑/etc/passwd文件完成。找到类似下面的一行:

git:x:503:503::/home/git:/bin/bash
改为:git:x:503:503::/home/git:/sbin/nologin

Git客户端推荐

TortoiseGit

对这只小乌龟估计没有开发人员会不认识,SVN的超广泛使用也使得这个超好用的Svn客户端成了几乎每个开发人员的桌面必备软件。小乌龟只提供Windows版本,提供中文版支持的,对于中国的开发者来说者绝对是福音。

小乌龟的文件管理器右键菜单的操作方式对于新手来说非常的容易上手,而且容易理解。

TortoiseGit(git客户端工具)

Sublime Merge for Mac(git客户端工具)

拥有简洁的界面,三向合并工具,强大搜索功能,语法突出显示等特点,Sublime Merge for Mac是一款Git客户端工具,完成了Sublime。与Sublime Text的制造商会面。一个快速的用户界面,三向合并工具,并排差异,语法高亮等等。Stage Files,iSwiftHunks和Lines没有等待 – Sublime Merge真的非常非常快。

Sublime Merge for Mac(git客户端工具)

GitHub for Desktop

全球开发人员交友俱乐部提供的强大工具,功能完善,使用方便。对于使用GitHub的开发人员来说是非常便捷的工具。界面干净,用起来非常顺手,上面的这条timeline非常漂亮,也可以直接提交PR。

唯一让我失望的是GitHub for Desktop不带三方合并工具,你必须自己手动解决冲突才可以。– 免费

– 同时支持 Windows 和 Mac:对于需要经常在不同的操作系统间切换的开发人员来说非常方便。

– 漂亮的界面:作为每天盯着看的工具,颜值是非常重要的

– 支持Pull Request:直接从客户端提交PR,很方便

– Timeline 支持:直接在时间线上显示每次提交的时间点和大小

– 支持git LFS:存储大文件更加节省空间和高效

– 不支持三方合并:需要借助第三方工具才行

Source Tree

SourceTree是老牌的Git GUI管理工具了,也号称是最好用的Git GUI工具。我的体验是确实强大,功能丰富,基本操作和高级操作都设计得非常流畅,适合初学者上手。

这个工具很有特色的一个功能就是支持Git Flow,你可以一键创建Git Flow的工作流。Git Flow是非常高效的团队协作模型和流程,Git的一大特色就是灵活轻量的分支,但如何在自己的团队中用好这个功能来匹配自己的研发流程是个问题。内置Git Flow让那些不太熟悉的开发人员也可以很快上手,并且将研发的业务流程固化在工具中,可以说是非常贴心的设计。

Source Tree(git客户端工具)

SmartGit:

商业用途收费, 个人用户免费:

推荐书籍

  • 《pro.git中文版》

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK