Git & Github

Git & Github

Git介绍

安装

仓库创建& 提交代码

代码回滚

工作区和暂存区

撤销修改

删除操作

远程仓库

分支管理

多人协作

github使用

忽略特殊文件.gitignore

 


版本控制

版本控制就是一个工具, 能记录每次对代码做了哪些修改, 并且可以轻易的把代码回滚到历史上的某个状态. 

版本控制工具主要实现2个功能:

版本管理

在开发中, 必须允许可以很容易对产品的版本进行任意回滚. 版本控制工具实现这个功能的原理简单来讲, 就是每修改一次代码, 它就做一次快照.

协作开发

在多人协作开发软件中, 版本控制能确保一直存储最新的代码库, 所有人的代码应该和最新的代码库保持一致.

常见版本管理工具介绍

1、VSS-- Visual Source Safe
此工具是Microsoft提供的,是使用的相当普遍的工具之一,他可以与VS.net进行无缝集成,成为了独立开发人员和小型开发团队所适合的工具,基本上Window平台上开发的中小型企业,当规模较大后,其性能通常是无法忍受的,对分支与并行开发支持的比较有限。

2、CVS--Concurrent Versions System,
此工具是一个开源工具,与后面提到的SVN是同一个厂家:Collab.Net提供的。
CVS是源于unix的版本控制工具,对于CVS的安装和使用最好对unix的系统有所了解能更容易学习,CVS的服务器管理需要进行各种命令行操作。目前,CVS的客户端有winCVS的图形化界面,服务器端也有CVSNT的版本,易用性正在提高。
此工具是相当著名,使用得相当广泛的版本控制工具之一,使用成熟的“Copy-Modify-Merge"开发模型,可以大大的提高开发效率,适合于项目比较大,产品发布频繁,分支活动频繁的中大型项目。

3、SVN --CollabNet Subversion
此工具是在CVS 的基础上,由CollabNet提供开发的,也是开源工具,应用比较广泛。
他修正cvs的一些局限性,适用范围同cvs,目前有一些基于SVN的第三方工具,如TortoiseSVN,是其客户端程序,使用的也相当广泛。在权限管理,分支合并等方面做的很出色,他可以与Apache集成在一起进行用户认证。
不过在权限管理方面目前还没有个很好用的界面化工具,SVNManger对于已经使用SVN进行配置的项目来说,基本上是无法应用的,但对于从头开始的项目是可以的,功能比较强大,但是搭建svnManger比较麻烦。
是一个跨平台的软件,支持大多数常见的操作系统。作为一个开源的版本控制系统,Subversion 管理着随时间改变的数据。 这些数据放置在一个中央资料档案库中。 这个档案库很像一个普通的文件服务器, 不过它会记住每一次文件的变动。 这样你就可以把档案恢复到旧的版本, 或是浏览文件的变动历史。Subversion 是一个通用的系统, 可用来管理任何类型的文件, 其中包括了程序源码。

4. GIT
因为最初是从Linux起家的,非常依赖文件系统的一些特性,这些在 Linux 下表现的很好,而 Windows 下特别糟糕Git 中文教程
Git是一个开源的分布式版本控制系统,用以有效、高速的处理从很小到非常大的项目版本管理.
Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
Torvalds 开始着手开发 Git 是为了作为一种过渡方案来替代 BitKeeper,后者之前一直是 Linux 内核开发人员在全球使用的主要源代码工具。开放源码社区中的有些人觉得 BitKeeper 的许可证并不适合开放源码社区的工作,因此 Torvalds 决定着手研究许可证更为灵活的版本控制系统。尽管最初 Git 的开发是为了辅助 Linux 内核开发的过程,但是我们已经发现在很多其他自由软件项目中也使用了 Git。例如 最近就迁移到 Git 上来了,很多 Freedesktop 的项目也迁移到了 Git 上。

5、BitKeeper
是由BitMover公司提供的,BitKeeper自称是“分布式”可扩缩SCM系统。
不是采用C/S结构,而是采用P2P结构来实现的,同样支持变更任务,所有变更集的操作都是原子的,与svn,cvs一致。

 


1. Git介绍

Git是目前世界上最先进的分布式版本控制系统.

Linus Torvalds在1991年创建了开源的Linux, 从此Linux系统不断发展, 已经成为最大的服务器系统软件了.

Linus虽然创建了Linux, 但Linux的壮大是靠全世界热心的志愿者参与的, 这么多人在世界各地为Linux编写代码, Linux的代码, 在2002年以前,世界各地的志愿者把源代码文件通过diff的方式发给Linus, 然后由Linus本人通过手工方式合并代码.

Linus坚定地反对CVS和SVN这些免费的版本控制系统, 这些集中式的版本控制系统不但速度慢, 而且必须联网才能使用. 有一些商用的版本控制系统, 虽然比CVS、SVN好用, 但是付费, 和Linux的开源精神不符.

到了2002年, Linux系统已经发展了十年了, 代码库之大让Linus很难继续通过手工方式管理了, 于是Linus选择了一个商业的版本控制系统BitKeeper, BitKeeper的BitMover公司授权Linux社区免费使用这个版本控制系统.

在2005年, 开发Samba的Andrew试图破解BitKeeper的协议, 被BitMover公司发现了, 于是BitMover公司要收回Linux社区的免费使用权.

于是Linus花了两周时间自己用C写了一个分布式版本控制系统, 这就是Git. 一个月之内, Linux系统的源码已经由Git管理了.

Git迅速成为最流行的分布式版本控制系统, 尤其是2008年, GitHub网站上线了(github是一个基于git的代码托管平台, 付费用户可以建私人仓库, 一般的免费用户只能使用公共仓库, 也就是代码要公开). 它为开源项目免费提供Git存储, 无数开源项目开始迁移至GitHub, 包括jQuery, PHP, RubyBootStrap, Node.js, CoffeScript等等. 

作为开源代码库以及版本控制系统, Github拥有超过900万开发者用户. 随着越来越多的应用程序转移到了云上, Github已经成为了管理软件开发以及发现已有代码的首选方法.

 


2. git安装

安装Git

最早Git是在Linux上开发的,很长一段时间内,Git也只能在Linux和Unix系统上跑。不过,慢慢地有人把它移植到了Windows上。现在,Git可以在Linux、Unix、Mac和Windows这几大平台上正常运行了。

用Debian或Ubuntu Linux,通过一条sudo apt-get install git就可以直接完成Git的安装,非常简单.

用Mac, 安装homebrew, 一条brew install git即可.

 


3. 版本库创建

创建目录

版本库又名仓库,英文名repository,可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪, 以便任何时刻都可以追踪历史,或者在将来某个时刻可以"还原".

创建一个版本库非常简单, 选择一个合适的地方, 创建一个空目录:

1
2
3
4
5
mkdir git_trainning
cd git_trainning/
 
$ git init
Initialized empty Git repository in /Users/Charon/documents/pycharmprojects/git_trainning/.git/

Git就建了一个空的仓库(empty Git repository). 当前目录下多了一个.git的目录, 这个目录是Git来跟踪管理版本库的.

.git目录默认是隐藏的, 用ls -ah命令就可以看见.

把文件添加到版本库

所有的版本控制系统, 只能跟踪文本文件的改动, 比如TXT文件, 网页, 所有的程序代码等等. 而图片, 视频, Microsoft的Word这些二进制文件, 虽然也能由版本控制系统管理, 但没法跟踪文件的变化, 只知道文件大小改变了.

Microsoft的Word格式是二进制格式,因此,版本控制系统是没法跟踪Word文件的改动的,前面我们举的例子只是为了演示,如果要真正使用版本控制系统,就要以纯文本方式编写文件。

编写一个first_git_file.txt文件, 内容如下:

1
2
3
4
$ vim first_git_file.txt
 
first time using git, excited!
第一次用git哈哈

一定要放到git_trainning目录下(子目录也行).

第一步,用命令git add告诉Git, 把文件添加到仓库:

1
$ git add first_git_file.txt 

执行上面的命令, 没有任何显示, 说明添加成功.

第二步, 用命令git commit告诉Git, 把文件提交到仓库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git commit -m "commit my first git file"
 
[master (root-commit) 621e6e4] commit my first git file
 Committer: Charon <Charon@Charon.local>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:
 
    git config --global --edit
 
After doing this, you may fix the identity used for this commit with:
 
    git commit --amend --reset-author
 
 file changed, 2 insertions(+)
 create mode 100644 first_git_file.txt

这里git直接通过主机名自己创建了一个, 可以通过git config --global --edit修改

git commit命令, -m后面输入的是本次提交的说明.

commit可以一次提交很多文件, 所以可以多次add不同的文件, 比如:

1
2
3
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."

  


4. 代码回滚

代码修改并提交

修改first_git_file.txt文件, 改成如下内容:

1
2
3
First time using git, excited! update ...
insert line here...
第一次用git哈哈

运行git status命令, 结果如下:

1
2
3
4
5
6
7
8
9
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
 
    modified:   first_git_file.txt
 
no changes added to commit (use "git add" and/or "git commit -a")

可以用git diff命令查看具体修改了什么内容:

1
2
3
4
5
6
7
8
9
10
11
$ git diff first_git_file.txt 
diff --git a/first_git_file.txt b/first_git_file.txt
index 2d13c2c..248d853 100644
--- a/first_git_file.txt
+++ b/first_git_file.txt
@@ -1,3 +1,4 @@
-first time using git, excited!
+First time using git, excited! update ...
 insert line here...
 第一次用git哈哈
+insert line again haha...

输出中+号绿色显示的就是修改或新增的内容, -号红色显示的就是去掉或被修改的内容.

提交修改和提交新文件是一样的两步, 第一步是git add

1
2
3
4
5
$ git add . # .  代表把当前目录下所有改动的文件都提交到代码库
Charon:git_trainning charon$ git commit -m "commit changes"
[master 50ad6b5] commit changes
 Committer: Charon <Charon@Charon.local>
 file changed, 1 insertion(+)

提交后再用git status命令查看仓库的当前状态:

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

Git显示当前没有需要提交的修改, 而且工作目录是干净的(working directory clean).

代码回滚

再修改first_git_file.txtt文件如下: 

1
2
3
4
5
First time using git, excited! update ...
insert line here..改之前的.
第一次用git哈哈
insert line again haha...
加点新内容 

然后提交:

1
2
3
4
5
$ git add first_git_file.txt 
$ git commit -m "add new content"
[master 4459657] add new content
 Committer: Charon <Charon@Charon.local>
 file changed, 2 insertions(+), 1 deletion(-)

Git的commit相当于保存一个快照, 一旦你把文件改乱了, 或者误删了文件, 可以从最近的一个commit恢复, 然后继续工作.

first_git_file.txt文件的几个被提交到Git仓库里的版本: 

版本1

1
2
first time using git, excited!
第一次用git哈哈

版本2

1
2
3
first time using git, excited!
insert line here...
第一次用git哈哈

版本3

1
2
3
4
first time using git, excited!
insert line here...
第一次用git哈哈
insert line again haha...

版本4

1
2
3
4
5
First time using git, excited! update ...
insert line here..改之前的.
第一次用git哈哈
insert line again haha...
加点新内容

在Git中, 可以用git log命令显示从最近到最远的提交日志:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ git log
commit 445965781d1fd0d91e76d120450dd18fd06c7489
Author: Charon <Charon@Charon.local>
Date:   Tue Jun 4 18:44:29 2018 +0800
 
    add new content
 
commit be02137bb2f54bbef0c2e99202281b3966251952
Author: Charon <Charon@Charon.local>
Date:   Tue Jun 4 17:55:16 2016 +0800
 
    update again
 
commit 50ad6b526810bb7ccfea430663757ba2337b9816
Author: Charon <Charon@Charon.local>
Date:   Tue Jun 4 17:46:51 2016 +0800
 
    commit changes
 
commit 621e6e44d04fa6a1cdc37826f01efa61b451abd1
Author: Charon <Charon@Charon.local>
Date:   Tue Jun 4 17:42:50 2016 +0800
 
    commit my first git file

可以加上--pretty=oneline参数:

1
2
3
4
5
$ git log --pretty=oneline
445965781d1fd0d91e76d120450dd18fd06c7489 add new content
be02137bb2f54bbef0c2e99202281b3966251952 update again
50ad6b526810bb7ccfea430663757ba2337b9816 commit changes
621e6e44d04fa6a1cdc37826f01efa61b451abd1 commit my first git file

一大串3628164...882e1e0是用SHA1计算出来的, 用十六进制表示的 - commit id(版本号).

 

回滚

在Git中, 用HEAD表示当前版本, 上一个版本就是HEAD^, 上上一个版本就是HEAD^^, 也可以写成HEAD~num

可以使用git reset命令把first_git_file.txt回退到上一个版本:

1
2
$ git reset --hard HEAD^
HEAD is now at be02137 update again

再用git log查看现在版本库的状态:

1
2
3
4
$ git log --pretty=oneline
be02137bb2f54bbef0c2e99202281b3966251952 update again
50ad6b526810bb7ccfea430663757ba2337b9816 commit changes
621e6e44d04fa6a1cdc37826f01efa61b451abd1 commit my first git file

最新的那个版本add new content已经看不到了, 但是只要上面的命令行窗口还没有被关掉, 可以顺着往上找, 找到那个add new content的commit id是445965781d1fd0d91e76d120450dd18fd06c7489

使用git reset命令指定回到某个版本:

1
2
git reset --hard 4459657
HEAD is now at 4459657 add new content

若是找不到想回退版本的commit id, 可以使用git reflog命令, 它记录了每一次命令:

1
2
3
4
5
6
7
8
9
10
11
$ git reflog
4459657 HEAD@{0}: reset: moving to 4459657
be02137 HEAD@{1}: reset: moving to HEAD^
4459657 HEAD@{2}: commit: add new content
be02137 HEAD@{3}: reset: moving to be02137bb
50ad6b5 HEAD@{4}: reset: moving to 50ad6b5
621e6e4 HEAD@{5}: reset: moving to 621e6e44
50ad6b5 HEAD@{6}: reset: moving to HEAD^
be02137 HEAD@{7}: commit: update again
50ad6b5 HEAD@{8}: commit: commit changes
621e6e4 HEAD@{9}: commit (initial): commit my first git file

 


5. 工作区和暂存区

工作区(Working Directory

就是你在电脑里能看到的目录,比如我的git_trainning文件夹就是一个工作区:

1
2
ls git_trainning/
first_git_file.txt

版本库(Repository)

工作区有一个隐藏目录.git, 这是Git的版本库.

Git的版本库里存了很多东西, 其中最重要的就是称为stage(或者叫index)的暂存区.

Git自动创建的第一个分支master, 以及指向master的一个指针叫HEAD.

把文件往Git版本库里添加分两步执行:

第一步是用git add把文件添加进去, 实际上就是把文件修改添加到暂存区;

第二步是用git commit提交更改, 实际上就是把暂存区的所有内容提交到当前分支.

创建Git版本库时, Git自动创建了唯一一个master分支, 所以现在git commit就是往master分支上提交更改.

(需要提交的文件修改通通放到暂存区, 然后一次性提交暂存区的所有修改)

 

先对first_git_file.txt做个修改, 加上一行内容:

1
2
3
4
5
6
First time using git, excited! update ...
insert line here..改之前的.
第一次用git哈哈
insert line again haha...
加点新内容
update v5

然后, 在工作区新增一个readme.txt文本文件.

先用git status查看一下状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
 
    modified:   first_git_file.txt
 
Untracked files:
  (use "git add <file>..." to include in what will be committed)
 
    readme.txt
 
no changes added to commit (use "git add" and/or "git commit -a")

Git清楚显示first_git_file.txt被修改了, 而readme.txt还从来没有被添加过, 所以它的状态是Untracked.

使用命令git add . 再用git status再查看:

1
2
3
4
5
6
7
8
$ git add .
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
 
    modified:   first_git_file.txt
    new file:   readme.txt

现在暂存区的状态就变成了这样:

所以git add命令实际上就是把要提交的所有修改放到暂存区(Stage), 然后执行git commit就可以一次性把暂存区的所有修改提交到分支.

1
2
3
4
$ git commit -m "知道暂存区stage的意思了"
[master 9d65cb2] 知道暂存区stage的意思了
 2 files changed, 2 insertions(+)
 create mode 100644 readme.md

一旦提交后, 工作区就是"干净"的:

1
2
3
$ git status
On branch master
nothing to commit, working directory clean

现在版本库变成了这样, 暂存区就没有任何内容:

 

 


6. 撤销修改

readme.md中添加了一行:

1
2
3
#git study repo
git is great 
but my stupid boss still prefers SVN.

git status查看一下:

1
2
3
4
5
6
7
8
9
git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
 
    modified:   readme.md
 
no changes added to commit (use "git add" and/or "git commit -a")

Git会显示git checkout -- filename可以丢弃工作区的修改:

1
2
3
4
$ git checkout -- readme.md 
 
more readme.md 
#git study repo

命令git checkout -- readme.md意思就是把readme.md文件在工作区的修改全部撤销, 这里有两种情况:

一种是readme.md修改后还没有被放到暂存区, 现在撤销修改就回到和版本库一模一样的状态;

一种是readme.md已经添加到暂存区后, 又作了修改, 现在撤销修改就回到添加到暂存区后的状态.

总之, 就是让这个文件回到最近一次git commitgit add时的状态.

在撤销命令git checkout -- filename中的切记要加--, 否则变成了"切换到另一个分支"的命令.

 

添加内容并git add .到暂存区了:

1
2
3
4
5
6
7
8
cat readme.md
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
My stupid boss still prefers SVN.
 
$ git add readme.md

git status查看, 修改只是添加到了暂存区, 还没有提交: 

1
2
3
4
5
6
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
 
    modified:   readme.md

Git显示用命令git reset HEAD file可以把暂存区的修改撤销掉(unstage), 重新放回工作区:

1
2
3
$ git reset HEAD readme.md 
Unstaged changes after reset:
M   readme.md

git reset命令既可以回退版本也可以把暂存区的修改回退到工作区. 当用HEAD时, 表示最新的版本.

再用git status查看, 现在暂存区是干净的, 工作区有修改

1
2
3
4
5
6
7
8
9
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   readme.md
#
no changes added to commit (use "git add" and/or "git commit -a")

再用git checkout -- filename可以丢弃工作区的修改:

1
2
3
4
$ git checkout -- readme.md 
 
more readme.md 
#git study repo

  


7. 删除操作

要从版本库中删除该文件, 就用命令git rm删掉并且git commit

1
2
3
4
5
6
7
x$ git rm test.txt
rm 'test.txt'
 
$ git commit -m "remove test"
[master 03df00a] remove test
 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 test.txt

若是删错了, 因为版本库里还有, 所以可以轻松地把误删的文件恢复到最新版本:

1
$ git checkout -- test.txt

git checkout其实是用版本库里的版本替换工作区的版本, 无论工作区是修改还是删除, 都可以"一键还原".

 


8. 远程仓库

Git是分布式版本控制系统, 同一个Git仓库, 可以分布到不同的机器上.

GitHub这个网站就是提供Git仓库托管服务的, 只要注册一个GitHub账号, 就可以免费获得Git远程仓库.

由于本地Git仓库和GitHub仓库之间的传输是通过SSH加密的, 所以需要一些设置:

第1步:创建SSH Key, 在用户主目录下出现.ssh目录, 这个目录下有id_rsaid_rsa.pub这两个文件:

1
ssh-keygen -t rsa -C "youremail@example.com"

也可以仅输, 然后一路回车即可

1
ssh-keygen -t rsa

在用户主目录里找到.ssh目录, 里面有id_rsaid_rsa.pub两个文件,这两个就是SSH Key的秘钥对, id_rsa是私钥, 保存于本地, id_rsa.pub是公钥, 需要将里面的内容伤处拿到远程仓库.

  • 输入cd指令,进入当前用户目录
  • 输入ls -a指令,查看当前用户目录下所有文件,包括隐藏文件
  • 输入cd .ssh指令,进入.ssh目录
  • 输入ls指令,查看.ssh目录下的文件
  • 输入cat id_rsa.pub指令,查看id_rsa.pub文件中内容

第2步:登陆GitHub, 打开"Account settings", "SSH and GPG Keys"页面:

然后, 点"Add SSH Key", 填上任意Title, 在Key文本框里粘贴id_rsa.pub文件的内容:

 

GitHub需要SSH Key来识别出推送提交人, 而Git支持SSH协议, 所以GitHub只要知道了公钥, 就可以确认提交人.

GitHub允许添加多个Key, 只要把每台电脑的Key都添加到GitHub, 就可以在每台电脑上往GitHub推送了.

创建远程仓库 

在本地创建了一个Git仓库后, 又想在GitHub创建一个Git仓库, 并且让这两个仓库进行远程同步, 这样GitHub上的仓库既可以作为备份, 又可以让其他人通过该仓库来协作.

首先, 登陆GitHub, 在右上角找到"New repository"按钮, 创建一个新的仓库:

创建好仓库后

现在在GitHub上的这个仓库还是空的, GitHub显示可以从这个仓库克隆出新的仓库, 也可以把一个已有的本地仓库与之关联并将本地仓库的内容推送到GitHub仓库.

根据GitHub的提示, 在本地已有的git_trainning仓库下运行命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ git remote add origin git@github.com:Charonnnnn/git_learning.git #添加远程仓库
$ git push -u origin master #推到远程
 
The authenticity of host 'github.com (192.30.253.113)' can't be established.
RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8.
Are you sure you want to continue connecting (yes/no)? yes   #第一次推会出现,写yes
Warning: Permanently added 'github.com,192.30.253.113' (RSA) to the list of known hosts.
Counting objects: 20, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (20/20), 1.76 KiB | 0 bytes/sdone.
Total 20 (delta 4), reused 0 (delta 0)
remote: Resolving deltas: 100% (4/4), done.
To git@github.com:Charonnnnn/git_learning.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from origin.

添加后, 远程库的名字就是origin, 这是Git默认的叫法.

把本地库的内容推送到远程, 用git push命令, 实际上是把当前分支master推送到远程.

 

要在本地作提交, 可以通过命令:

1
$ git push origin master

创建一个index.html文件, 同时上传到远程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ vim index.html
$ git add .
$ git commit -m "add home page"
 
[master 8675486] add home page
 file changed, 6 insertions(+)
 create mode 100644 index.html
 
$ git push origin master #推到远程
 
Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 362 bytes | 0 bytes/sdone.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:Charonnnnn/git_learning.git
   03df00a..8675486  master -> master

然后刷新下远程仓库(Github)页面, 就可以看到新创建的文件:

从远程库克隆

首先, 登陆GitHub, 创建一个新的仓库, 名字叫gitskills

勾选Initialize this repository with a README, 这样GitHub会自动创建一个README.md文件. 创建完毕后,可以看到README.md文件:

用命令git clone克隆一个本地库:

在本地找一个你想存放这个远程仓库的目录,然后在本地命令行用git clone 命令来克隆这个远程库

1
2
3
4
5
6
7
8
9
10
11
$ git clone git@github.com:triaquae/gitskills.git 
Cloning into 'gitskills'...
Warning: Permanently added the RSA host key for IP address '192.30.253.112' to the list of known hosts.
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
Checking connectivity... done.
 
cd gitskills/  #进入刚clone下来的目录
ls
README.md

如果有多个人协作开发, 那么每个人各自从远程克隆一份就可以了.

 


9. 分支管理

分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。

现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。

Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。

创建与合并分支 

每次提交, Git都把版本串成一条时间线, 这条时间线就是一个分支. 刚创建的仓库只有一条时间线, 在Git里, 这个分支叫主分支, 即master分支. HEAD是指向master, master才是指向提交的, 所以HEAD指向的就是当前分支.

一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:

每次提交, master分支都会向前移动一步, 随着你不断提交, master分支的线也越来越长, 当创建新的分支, 例如dev时, Git新建了一个指针叫dev, 指向master相同的提交, 再把HEAD指向dev, 就表示当前分支在dev上: 

假如在dev上的工作完成了, 就可以把dev合并到master上. 最简单的方法就是直接把master指向dev的当前提交, 就完成了合并:

所以Git合并分支很快, 就改改指针, 工作区内容也不变.

合并完分支后, 甚至可以删除dev分支. 删除dev分支就是把dev指针给删掉, 删掉后就剩下了一条master分支:

 

首先, 创建dev分支, 然后切换到dev分支:

1
2
$ git checkout -b dev
Switched to a new branch 'dev'

git checkout命令加上-b参数表示创建并切换, 相当于以下两条命令:

1
2
3
$ git branch dev
$ git checkout dev
Switched to branch 'dev'

git branch命令查看当前分支:

1
2
3
$ git branch
* dev
  master

git branch命令会列出所有分支, 当前分支前面会标一个*号.

然后就可以在dev分支上正常提交, 比如对readme.txt做个修改, 加上一行:

1
Creating a new branch is quick.

提交:

1
2
3
4
$ git add readme.txt 
$ git commit -m "branch test"
[dev fec145a] branch test
 file changed, 1 insertion(+)

现在dev分支的工作完成, 就可以切换回master分支:

1
2
$ git checkout master
Switched to branch 'master'

切换回master分支后, 再查看一个readme.txt文件, 刚才添加的内容不见了, 因为那个提交是在dev分支上, 而master分支此刻的提交点并没有变:

现在把dev分支的工作成果合并到master分支上:

1
2
3
4
5
$ git merge dev
Updating d17efd8..fec145a
Fast-forward
 readme.txt |    1 +
 file changed, 1 insertion(+)

git merge命令用于合并指定分支到当前分支. 合并后, 再查看readme.txt的内容, 可以看到和dev分支的最新提交是完全一样的.

注意到上面的Fast-forward信息, Git显示这次合并是"快进模式", 也就是直接把master指向dev的当前提交, 所以合并速度非常快.

合并完成后就可以删除dev分支了:

1
2
$ git branch -d dev
Deleted branch dev (was fec145a).

删除后查看branch就只剩下master分支:

1
2
$ git branch
* master

因为创建、合并和删除分支非常快, 所以Git鼓励使用分支完成某个任务, 合并后再删掉分支, 这和直接在master分支上工作效果是一样的, 但过程更安全.

解决冲突

新的feature1分支:

1
2
$ git checkout -b feature1
Switched to a new branch 'feature1'

修改readme.txt最后一行:

1
added this line from branch feature 1

feature1分支上提交:

$ git add readme.txt 
$ git commit -m "add feature"
[feature1 75a857c] AND simple
 1 file changed, 1 insertion(+), 1 deletion(-)

切换到master分支:

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.

Git还会自动提示当前master分支比远程的master分支要超前1个提交.

master分支上把readme.txt文件的最后一行改为:

1
added this line from master

提交:

$ git add readme.txt 
$ git commit -m "master update"
[master 400b400] & simple
 1 file changed, 1 insertion(+), 1 deletion(-)

现在master分支和feature1分支各自都分别有新的提交, 变成了这样:

这种情况下Git无法执行"快速合并", 只能试图把各自的修改合并起来, 但这种合并就可能会有冲突:

1
2
3
4
$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

Git显示readme.txt文件存在冲突, 必须手动解决冲突后再提交. git status也可以告诉显示冲突的文件:

1
2
3
4
5
6
7
8
9
10
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

可以直接查看readme.txt的内容:

1
2
3
4
5
6
7
#git study repo
Creating a new branch is quick.
<<<<<<< HEAD
added this line from master
=======
added this line from branch feature 1
>>>>>>> feature1

Git用<<<<<<<=======>>>>>>>标记出不同分支的内容, 修改如下后保存:

1
2
3
4
#git study repo
Creating a new branch is quick.
added this line from master
added this line from branch feature 1

再提交

1
2
3
$ git add readme.txt 
$ git commit -m "conflict fixed"
[master 59bc1cb] conflict fixed

现在master分支和feature1分支变成了下图所示:

用带参数的git log也可以看到分支的合并情况:

1
2
3
4
5
6
7
8
$ git log --graph --pretty=oneline
*   feedd786cad3e18323a41846fcc1b0d52fc0c98e fix conflict
|\  
| * 01f8f8d168e113fac9fbe24c4cfa6d4c351a9821 update from branch
* | 743ccee30f3d74f1993f17e7312032b7399b1306 from master
|/  
* edfbc29982927236596539e0f1971b0575f803c0 branch test
* 8675486bfeeb340914369e80d2cfcf3e854e88a3 add home page  

分支策略

在实际开发中, 应该按照几个基本原则进行分支管理:

首先,master分支应该是非常稳定的, 也就是仅用来发布新版本, 平时不能在上面修改;

修改都在dev分支上, 即dev分支是不稳定的. 发布时, 再把dev分支合并到master上, 在master分支发布1.0版本;

每个人都在dev分支上干活, 每个人都有自己的分支, 时不时地往dev分支上合并就可以了, 所以团队合作的分支看起来就像这样:

bug分支

每个bug都可以通过一个新的临时分支来修复, 修复后合并分支, 然后将临时分支删除.

要创建一个bug分支issue-101来修复bug时, 当前正在dev上进行的工作还没有提交:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ git status
# On branch dev
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   hello.py
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   readme.txt
#

Git提供了一个stash功能, 可以把当前工作现场“储藏”起来, 等以后恢复现场后继续工作:

1
2
3
$ git stash
Saved working directory and index state WIP on dev: 6224937 add merge
HEAD is now at 6224937 add merge

首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:

1
2
3
4
5
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
$ git checkout -b issue-101
Switched to a new branch 'issue-101'

现在修复bug,需要把“Git is free software ...”改为“Git is a free software ...”,然后提交:

1
2
3
4
$ git add readme.txt 
$ git commit -m "fix bug 101"
[issue-101 cc17032] fix bug 101
 file changed, 1 insertion(+), 1 deletion(-)

修复完成后,切换到master分支,并完成合并,最后删除issue-101分支:

1
2
3
4
5
6
7
8
9
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 2 commits.
$ git merge --no-ff -m "merged bug fix 101" issue-101
Merge made by the 'recursive' strategy.
 readme.txt |    2 +-
 file changed, 1 insertion(+), 1 deletion(-)
$ git branch -d issue-101
Deleted branch issue-101 (was cc17032).

回到dev分支:

1
2
3
4
5
$ git checkout dev
Switched to branch 'dev'
$ git status
# On branch dev
nothing to commit (working directory clean)

git stash list命令看看:

1
2
$ git stash list
stash@{0}: WIP on dev: 6224937 add merge

工作现场还在, Git把stash内容存在某个地方了, 但是需要恢复一下. 有两个办法:

一是用git stash apply恢复, 但是恢复后stash内容并不删除, 需要用git stash drop来删除;

另一种方式是用git stash pop, 恢复的同时把stash内容也删了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git stash pop
# On branch dev
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   hello.py
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   readme.txt
#
Dropped refs/stash@{0} (f624f8e5f082f2df2bed8a4e09c12fd2943bdd40)

再用git stash list查看, 就看不到任何stash内容了:

1
$ git stash list

可以多次stash, 恢复的时候先用git stash list查看, 然后恢复指定的stash,用命令:

1
$ git stash apply stash@{0}

  


10. 多人协作

当从远程仓库克隆时实际上Git自动把本地的master分支和远程的master分支对应起来了, 并且远程仓库的默认名称是origin.

要查看远程库的信息, 用git remote指令

1
2
$ git remote
origin

或者用git remote -v显示更详细的信息:

1
2
3
$ git remote -v
origin  git@github.com:triaquae/gitskills.git (fetch)
origin  git@github.com:triaquae/gitskills.git (push)  

上面显示了可以抓取和推送的origin的地址. 如果没有推送权限,就看不到push的地址.

推送分支

推送分支就是把该分支上的所有本地提交推送到远程库. 推送时, 要指定本地分支, 这样Git就会把该分支推送到远程库对应的远程分支上:

1
$ git push origin master

如果要推送其他分支, 比如dev,就改成:

1
$ git push origin dev

但是并不是一定要把本地分支往远程推送:

  • master分支是主分支,因此要时刻与远程同步;

  • dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;

  • bug分支只用于在本地修复bug,就没必要推到远程了;

  • feature分支是否推到远程,取决于是否合作在上面开发.

抓取分支

多人协作时,大家都会往masterdev分支上推送各自的修改。

现在,模拟一个你的小伙伴,可以在另一台电脑(注意要把SSH Key添加到GitHub)或者同一台电脑的另一个目录下克隆:

1
2
3
4
5
6
7
$ git clone git@github.com:triaquae/gitskills.git
Cloning into 'gitskills'...
remote: Counting objects: 16, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 16 (delta 0), reused 10 (delta 0), pack-reused 0
Receiving objects: 100% (16/16), done.
Checking connectivity... done.

当你的小伙伴从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支。不信可以用git branch命令看看:

1
2
$ git branch
* master

现在,你的小伙伴要在dev分支上开发,就必须创建远程origindev分支到本地,于是他用这个命令创建本地dev分支:

1
$ git checkout -b dev origin/dev

现在,他就可以在dev上继续修改,然后,时不时地把dev分支push到远程:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ git add .
$ git commit -m "small updates"
 
[dev f1b762e] small updates
 2 files changed, 5 insertions(+), 1 deletion(-)
Alexs-MacBook-Pro:gitskills alex$ git push origin dev
Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 438 bytes | 0 bytes/sdone.
Total 4 (delta 0), reused 0 (delta 0)
To git@github.com:triaquae/gitskills.git
   33ec6b4..f1b762e  dev -> dev

你的小伙伴已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ git add .
$ git commit -m "add Dog class"
[dev 7e7b1bf] add Dog class
 2 files changed, 7 insertions(+)
 
 
$ git push origin dev
 
To git@github.com:triaquae/gitskills.git
 ! [rejected]        dev -> dev (fetch first)
error: failed to push some refs to 'git@github.com:triaquae/gitskills.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已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ git pull
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 0), reused 4 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), done.
From github.com:triaquae/gitskills
   33ec6b4..f1b762e  dev        -> origin/dev
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
 
    git pull <remote> <branch>
 
If you wish to set tracking information for this branch you can do so with:
 
    git branch --set-upstream-to=origin/<branch> dev

git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置devorigin/dev的链接:

1
2
$ git branch --set-upstream-to=origin/dev dev
Branch dev set up to track remote branch dev from origin.

再pull:

1
2
3
4
5
6
$ git pull
Auto-merging hello.py
CONFLICT (content): Merge conflict in hello.py
Auto-merging branch_test.md
CONFLICT (content): Merge conflict in branch_test.md
Automatic merge failed; fix conflicts and then commit the result.

这回git pull成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的解决冲突完全一样。解决后,提交,再push:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git add .
$ git commit -m "merge & fix hello.py"
[dev 93e28e3] merge & fix hello.py
 
$ git push origin dev
 
Counting objects: 8, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (7/7), done.
Writing objects: 100% (8/8), 819 bytes | 0 bytes/sdone.
Total 8 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), done.
To git@github.com:triaquae/gitskills.git
   f1b762e..93e28e3  dev -> dev

因此,多人协作的工作模式通常是这样:

  1. 首先,可以试图用git push origin branch-name推送自己的修改;

  2. 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;

  3. 如果合并有冲突,则解决冲突,并在本地提交;

  4. 没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功!

如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream branch-name origin/branch-name

这就是多人协作的工作模式,一旦熟悉了,就非常简单。

  


11. Github使用

GitHub作为免费的远程仓库, 如果是个人的开源项目, 放到GitHub上是完全没有问题的. GitHub还是一个开源协作社区, 通过GitHub既可以让别人参与你的开源项目, 也可以参与别人的开源项目.

在GitHub上利用Git极其强大的克隆和分支功能, 每个人都可以自由参与各种开源项目.

假设参与bootstrap开源项目, 可以访问它的项目主页https://github.com/twbs/bootstrap, 点“Fork”就在自己的账号下克隆了一个bootstrap仓库, 然后从自己的账号下clone:

1
git clone git@github.com:Charonnnnn/bootstrap.git

一定要从自己的账号下clone仓库, 这样才能推送修改. 如果从bootstrap的作者的仓库地址git@github.com:twbs/bootstrap.git克隆, 因为没有权限, 将不能推送修改.

Bootstrap的官方仓库twbs/bootstrap、你在GitHub上克隆的仓库my/bootstrap,以及你自己克隆到本地电脑的仓库, 关系就像下图显示:

若想修复bootstrap的一个bug或者新增一个功能, 结束后往自己的仓库推送.

若希望bootstrap的官方库能接受你的修改, 可以在GitHub上发起一个pull request.

小结

  • 在GitHub上,可以任意Fork开源仓库;

  • 自己拥有Fork后的仓库的读写权限;

  • 可以推送pull request给官方仓库来贡献代码。

  


12. 忽略特殊文件.gitignore

有些时候必须把某些文件放到Git工作目录中, 但又不能提交它们, 比如保存了数据库密码的配置文件等等, 每次git status都会显示Untracked files ...

解决方法: 在Git工作区的根目录下创建一个特殊的.gitignore文件, 然后把要忽略的文件名填进去, Git就会自动忽略这些文件.

不需要从头写.gitignore文件,GitHub已经准备了各种配置文件, 只需要组合一下就可以使用了. 所有配置文件可以直接在线浏览:https://github.com/github/gitignore

忽略文件的原则是:

  1. 忽略操作系统自动生成的文件,比如缩略图等;
  2. 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
  3. 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件.

举个例子:

假设在Windows下进行Python开发, Windows会自动在有图片的目录下生成隐藏的缩略图文件, 如果有自定义目录, 目录下就会有Desktop.ini文件, 因此需要忽略Windows自动生成的垃圾文件:

1
2
3
4
# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini

然后还要忽略Python编译产生的.pyc.pyodist等文件或目录:

1
2
3
4
5
6
7
# Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build

加上自己定义的文件, 最终得到一个完整的.gitignore文件, 内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini
 
# Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build
 
# My configurations:
db.ini
deploy_key_rsa

最后一步就是把.gitignore也提交到Git. 检验.gitignore的标准是git status命令是不是说working directory clean.

有些时候想添加一个文件到Git, 但发现添加不了的原因是这个文件被.gitignore忽略了:

1
2
3
4
$ git add App.class
The following paths are ignored by one of your .gitignore files:
App.class
Use -f if you really want to add them.

如果确实想添加该文件,可以用-f强制添加到Git:

1
$ git add -f App.class

或者发现可能是.gitignore写得有问题, 需要找出来到底哪个规则写错了, 可以用git check-ignore命令检查:

1
2
$ git check-ignore -v App.class
.gitignore:3:*.class    App.class  

Git会显示.gitignore的第3行规则忽略了该文件, 这样就可以知道应该修订哪个规则.

小结

  • 忽略某些文件时,需要编写.gitignore

  • .gitignore文件本身要放到版本库里,并且可以对.gitignore做版本管理!

 


 

git init   初始化

git add filename 添加到暂存区
git commit -m “comment”  添加到仓库

git config --global user.email “xxx”
git config --global user.name “xxxx”


git status
git diff 在git add之前

git log  显示从最近到最远的提交日志
git log --pretty=oneline

git reset --hard HEAD^ 回滚到上个版本 HEAD~num
git reset --hard hjF24ng  回滚到具体版本

git reflog 记录每次命令

git checkout -- filename   在工作区是可以撤销修改
git reset HEAD filename  将file从暂存区放回找工作区 再执行上面撤销指令

git rm filename
git commit -m “xx”

ssh-keygen -t rsa 获取ssh key   一路回车
 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFhVAAOaRsEGASFALnChWwcsq5fNMmFG8rQttpnSaIiCFCUYSIFCKFmpPVD/0XBwyE2VLrv3Tf0DT4esdGYmf2YErchGSNjcWNh3FVIQqDow57Ji3qjmYFzAvh4FrcshVKcQRii7CFHT5XY/sHpoXL7w+Zu9VyPpifW8pkVspug20srUoMi3T35wlTx5hKjlCN6Gt3XIO/rSv6qLRO5YdhhaIThsIQ38j00yPtpGArnQu4BSy+65wuzDlT/FhXIhao90g97Ap/A9DQd9kPPO6E4HBerRIwYm6kk0MzcG3CihAfY3vIHon15C8jfiB81Zdtgz0d+RLFa60sUXmGo2iz Charon@Charon.local

git remote add origin https://github.com/Charonnnnn/git_learning.git
git pull --rebase origin master git push
-u origin master git clone git@github.com:Charonnnnn/git_learing_2.git git push origin branchname 将现库的代码推到远程库 git pull 将远程库的代码拉下来 git checkout -b branchname git checkout branchname git branch git merge branchname 合并分支, 要先切换到master git branch -d branchnamr 删除分支
# 服务器上传
如果希望保留生产服务器上所做的改动,仅仅并入新配置项, 处理方法如下:
git stash
git pull
git stash pop
然后可以使用git diff -w +文件名 来确认代码自动合并的情况.

反过来,如果希望用代码库中的文件完全覆盖本地工作版本. 方法如下:
git reset --hard
git pull
其中git reset是针对版本,如果想针对文件回退本地修改,使用
git checkout HEAD file/to/restore  

 

https://blog.csdn.net/xiaohanluo/article/details/53214933

https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/001373962845513aefd77a99f4145f0a2c7a7ca057e7570000

posted @ 2018-05-22 01:56  Charonnnnn  阅读(162)  评论(0)    收藏  举报