前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【读书笔记】廖雪峰:Git教程

【读书笔记】廖雪峰:Git教程

作者头像
辉哥
发布2019-04-21 15:50:42
1.1K0
发布2019-04-21 15:50:42
举报
文章被收录于专栏:区块链入门区块链入门

1,摘要

本文是辉哥学习廖雪峰的《Git教程》的读书笔记,把其中一些精要的命令记录了下来。这个笔记主要给辉哥自己做备忘索引,对一般人来说很难有明了的价值。建议直接点击学习廖雪峰的文章《Git教程》

2,GIT基本操作

2.1 安装GIT

<1> linux
代码语言:javascript
复制
$ git
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

$sudo apt-get install git
<2> WINDOWS

在Windows上使用Git,可以从Git官网直接下载安装程序

<3> git的帮助命令
代码语言:javascript
复制
E:\temp>git
usage: git [--version] [--help] [-C <path>] [-c <name>=<value>]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p | --paginate | -P | --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

work on the current change (see also: git help everyday)
   add        Add file contents to the index
   mv         Move or rename a file, a directory, or a symlink
   reset      Reset current HEAD to the specified state
   rm         Remove files from the working tree and from the index

examine the history and state (see also: git help revisions)
   bisect     Use binary search to find the commit that introduced a bug
   grep       Print lines matching a pattern
   log        Show commit logs
   show       Show various types of objects
   status     Show the working tree status

grow, mark and tweak your common history
   branch     List, create, or delete branches
   checkout   Switch branches or restore working tree files
   commit     Record changes to the repository
   diff       Show changes between commits, commit and working tree, etc
   merge      Join two or more development histories together
   rebase     Reapply commits on top of another base tip
   tag        Create, list, delete or verify a tag object signed with GPG

collaborate (see also: git help workflows)
   fetch      Download objects and refs from another repository
   pull       Fetch from and integrate with another repository or a local branch
   push       Update remote refs along with associated objects

'git help -a' and 'git help -g' list available subcommands and some
concept guides. See 'git help <command>' or 'git help <concept>'
to read about a specific subcommand or concept.
<4> git环境配置
代码语言:javascript
复制
$ git config --global user.name "duncanwang"
$ git config --global user.email "wangdenghui2005@sina.com"

2.2 创建版本库

<1> 创建文件夹

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

代码语言:javascript
复制
$ mkdir learngit
$ cd learngit
$ pwd
/Users/michael/learngit

【说明】如果你使用Windows系统,为了避免遇到各种莫名其妙的问题,请确保目录名(包括父目录)不包含中文。

<2> git init初始化仓库

通过git init命令把这个目录变成Git可以管理的仓库:

代码语言:javascript
复制
$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/

【说明】当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的

2.3 把文件添加到版本库

<1> 命令git add告诉Git,把文件添加到仓库
代码语言:javascript
复制
$ git add readme.txt

【说明】注意,可反复多次使用,添加多个文件;

<2> 用命令git commit告诉Git,把文件提交到仓库:
代码语言:javascript
复制
$ git commit -m "wrote a readme file"
[master (root-commit) eaadf4e] wrote a readme file
 1 file changed, 2 insertions(+)
 create mode 100644 readme.txt

2.4 仓库管理

(1)运行git status命令看看结果

先修改readme.txt文件:

代码语言:javascript
复制
Git is a distributed version control system.
Git is free software.

然后查询status状态:

代码语言:javascript
复制
$ 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.txt

no changes added to commit (use "git add" and/or "git commit -a")
(2)git diff查看差异

git diff查看本地和仓库的区别。

代码语言:javascript
复制
$ git diff readme.txt 
diff --git a/readme.txt b/readme.txt
index 46d49bf..9247db6 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
-Git is a version control system.
+Git is a distributed version control system.
 Git is free software.
(3)git log命令查看修改记录
代码语言:javascript
复制
$ git log
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 21:06:15 2018 +0800

    append GPL

commit e475afc93c209a690c39c13a46716e8fa000c366
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 21:03:36 2018 +0800

    add distributed

commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 20:59:18 2018 +0800

    wrote a readme file

如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline参数:

代码语言:javascript
复制
$ git log --pretty=oneline
1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master) append GPL
e475afc93c209a690c39c13a46716e8fa000c366 add distributed
eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0 wrote a readme file
(5)版本回退

我们要把当前版本append GPL回退到上一个版本add distributed,就可以使用git reset命令:

代码语言:javascript
复制
$ git reset --hard HEAD^
HEAD is now at e475afc add distributed

在Git中,用HEAD表示当前版本,上一个版本就是HEAD,上上一个版本就是HEAD,当然往上100个版本写100个比较容易数不过来,所以写成HEAD~100。

版本恢复:

代码语言:javascript
复制
$ git reset --hard 1094a
HEAD is now at 83b0afe append GPL

Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向append GPL:

┌────┐ │HEAD│ └────┘ │ └──> ○ append GPL │ ○ add distributed │ ○ wrote a readme file

改为指向add distributed:

┌────┐ │HEAD│ └────┘ │ │ ○ append GPL │ │ └──> ○ add distributed │ ○ wrote a readme file

(6)命令记录git reflog

Git提供了一个命令git reflog用来记录你的每一次命令:

代码语言:javascript
复制
$ git reflog
e475afc HEAD@{1}: reset: moving to HEAD^
1094adb (HEAD -> master) HEAD@{2}: commit: append GPL
e475afc HEAD@{3}: commit: add distributed
eaadf4e HEAD@{4}: commit (initial): wrote a readme file

从这儿可以找到曾经的命令,可用于恢复到新版本。

(7)版本库(Repository)和工作区

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

前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:

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

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

(8)丢弃工作区和丢弃暂存区

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

代码语言:javascript
复制
$ git checkout -- readme.txt

git checkout -- file命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令

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

代码语言:javascript
复制
$ git reset HEAD readme.txt
Unstaged changes after reset:
M    readme.txt

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

场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。

(9)删除文件git rm

先本地删除文件。

一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit:

代码语言:javascript
复制
$ git rm test.txt
rm 'test.txt'

$ git commit -m "remove test.txt"
[master d46f35e] remove test.txt
 1 file changed, 1 deletion(-)
 delete mode 100644 test.txt

现在,文件就从版本库中被删除了。

另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:

代码语言:javascript
复制
$ git checkout -- test.txt

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

3.远程仓库(GitHub为例)

3.1 连接远程仓库配置GitHub

第1步:创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:

代码语言:javascript
复制
$ ssh-keygen -t rsa -C "wangdenghui2005@sina.com"

【注意】是Git Bash运行命令,而不是用CMD运行命令,3次回车,不设置密码吧。

如果一切顺利的话,可以在用户主目录(辉哥的目录为:C:\Users\dd)里找到.ssh目录,有id_rsa和id_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。

第2步:登陆GitHub,打开“Account settings”,“SSH Keys”页面: 然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容:

点“Add Key”,你就应该看到已经添加的Key:

3.2 添加远程库

你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步。

第一步: 登陆GitHub,然后,在右上角找到“Create a new repo”按钮,创建一个新的仓库:

在Repository name填入learngit,其他保持默认设置,点击“Create repository”按钮,就成功地创建了一个新的Git仓库:

第二步: 在本地的learngit仓库下运行命令:

代码语言:javascript
复制
$ git remote add origin git@github.com:duncanwang/learngit.git

请千万注意,把上面的duncanwang替换成你自己的GitHub账户名, 添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,例如Github.

第三步,就可以把本地库的所有内容推送到远程库上:

代码语言:javascript
复制
$ git push -u origin master
Counting objects: 20, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (20/20), 1.64 KiB | 560.00 KiB/s, done.
Total 20 (delta 5), reused 0 (delta 0)
remote: Resolving deltas: 100% (5/5), done.
To github.com:michaelliao/learngit.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

【说明】我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

代码语言:javascript
复制
E:\temp\learngit>git push -u origin master
Everything up-to-date
Branch 'master' set up to track remote branch 'master' from 'origin'.

3.3 从远程库克隆

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

代码语言:javascript
复制
$ git clone git@github.com:duncanwang/learngit.git
Cloning into 'gitskills'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 3
Receiving objects: 100% (3/3), done.

还可以使用https的命令: git clone https://github.com/duncanwang/learngit

4. 分支管理

4.1 创建与合并分支概念

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

当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:

从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:

4.2 创建分支git checkout -b

我们创建dev分支,然后切换到dev分支:

代码语言:javascript
复制
$ git checkout -b dev
Switched to a new branch 'dev'

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

代码语言:javascript
复制
$ git branch dev
$ git checkout dev
Switched to branch 'dev'

4.3 查看分支git branch

代码语言:javascript
复制
$ git branch
* dev
  master

4.4 合并分支git merge

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

代码语言:javascript
复制
Creating a new branch is quick.

然后提交:

代码语言:javascript
复制
$ git add readme.txt 
$ git commit -m "branch test"
[dev b17d20e] branch test
 1 file changed, 1 insertion(+)

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

代码语言:javascript
复制
$ git checkout master
Switched to branch 'master'

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

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

代码语言:javascript
复制
$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

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

注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。

当然,也不是每次合并都能Fast-forward,我们后面会讲其他方式的合并。

4.5 删除分支git branch -d

代码语言:javascript
复制
$ git branch -d dev
Deleted branch dev (was b17d20e).

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

代码语言:javascript
复制
$ git branch
* master

4.6 解决冲突

准备新的feature1分支,继续我们的新分支开发:

代码语言:javascript
复制
$ git checkout -b feature1
Switched to a new branch 'feature1'

修改readme.txt最后一行,改为:

代码语言:javascript
复制
Creating a new branch is quick AND simple.

在feature1分支上提交:

代码语言:javascript
复制
$ git add readme.txt

$ git commit -m "AND simple"
[feature1 14096d0] AND simple
 1 file changed, 1 insertion(+), 1 deletion(-)

切换到master分支:

代码语言:javascript
复制
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

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

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

代码语言:javascript
复制
Creating a new branch is quick & simple.

提交:

代码语言:javascript
复制
$ git add readme.txt 
$ git commit -m "& simple"
[master 5dc6824] & simple
 1 file changed, 1 insertion(+), 1 deletion(-)

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

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

代码语言:javascript
复制
$ 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也可以告诉我们冲突的文件:

代码语言:javascript
复制
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

    both modified:   readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

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

代码语言:javascript
复制
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.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1

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

代码语言:javascript
复制
Creating a new branch is quick and simple.

再提交:

代码语言:javascript
复制
$ git add readme.txt 
$ git commit -m "conflict fixed"
[master cf810e4] conflict fixed

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

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

代码语言:javascript
复制
$ git log --graph --pretty=oneline --abbrev-commit
*   cf810e4 (HEAD -> master) conflict fixed
|\  
| * 14096d0 (feature1) AND simple
* | 5dc6824 & simple
|/  
* b17d20e branch test
* d46f35e (origin/master) remove test.txt
* b84166e add test.txt
* 519219b git tracks changes
* e43a48b understand how stage works
* 1094adb append GPL
* e475afc add distributed
* eaadf4e wrote a readme file

4.7 分支管理策略

通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。

如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。

下面我们实战一下--no-ff方式的git merge:

首先,仍然创建并切换dev分支:

代码语言:javascript
复制
$ git checkout -b dev
Switched to a new branch 'dev'

修改readme.txt文件,并提交一个新的commit:

代码语言:javascript
复制
$ git add readme.txt 
$ git commit -m "add merge"
[dev f52c633] add merge
 1 file changed, 1 insertion(+)

现在,我们切换回master:

代码语言:javascript
复制
$ git checkout master
Switched to branch 'master'

准备合并dev分支,请注意--no-ff参数,表示禁用Fast forward:

代码语言:javascript
复制
$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。 合并后,我们用git log看看分支历史:

代码语言:javascript
复制
$ git log --graph --pretty=oneline --abbrev-commit
*   e1e9c68 (HEAD -> master) merge with no-ff
|\  
| * f52c633 (dev) add merge
|/  
*   cf810e4 conflict fixed
...

可以看到,不使用Fast forward模式,merge后就像这样:

分支策略

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

那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;

你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。

所以,团队合作的分支看起来就像这样:

git-br-policy

4.8 保存工作现场git stash

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

代码语言:javascript
复制
$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge

用git stash list命令看看:

代码语言:javascript
复制
$ git stash list
stash@{0}: WIP on dev: f52c633 add merge

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

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

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

代码语言:javascript
复制
$ 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} (5d677e2ee266f39ea296182fb2354265b91b3b2a)

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

代码语言:javascript
复制
$ git stash apply stash@{0}

4.9 多人协作git push

(1)推送分支git push

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

代码语言:javascript
复制
$ git push origin master

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

代码语言:javascript
复制
$ git push origin dev

但是,并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?

  • master分支是主分支,因此要时刻与远程同步;
  • dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
  • bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
  • feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

总之,就是在Git中,分支完全可以在本地自己藏着玩,是否推送,视你的心情而定!

抓取分支

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

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

代码语言:javascript
复制
$ git checkout -b dev origin/dev

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

代码语言:javascript
复制
$ git add env.txt

$ git commit -m "add env"
[dev 7a5e5dd] add env
 1 file changed, 1 insertion(+)
 create mode 100644 env.txt

$ git push origin dev
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 308 bytes | 308.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
   f52c633..7a5e5dd  dev -> dev

后面你修改后push时出现推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送:

代码语言:javascript
复制
$ git pull
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分支的链接,根据提示,设置dev和origin/dev的链接:

代码语言:javascript
复制
$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

再pull:

代码语言:javascript
复制
$ git pull
Auto-merging env.txt
CONFLICT (add/add): Merge conflict in env.txt
Automatic merge failed; fix conflicts and then commit the result.

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

代码语言:javascript
复制
$ git commit -m "fix env conflict"
[dev 57c53ab] fix env conflict

$ git push origin dev
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 621 bytes | 621.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
   7a5e5dd..57c53ab  dev -> dev

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

  • 首先,可以试图用git push origin <branch-name>推送自己的修改;
  • 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
  • 如果合并有冲突,则解决冲突,并在本地提交;
  • 没有冲突或者解决掉冲突后,再用git push origin <branch-name>推送就能成功!

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

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

5. 标签管理

5.1 创建标签git tag

在Git中打标签非常简单,首先,切换到需要打标签的分支上:

代码语言:javascript
复制
$ git branch
* dev
  master
$ git checkout master
Switched to branch 'master'

然后,敲命令git tag <name>就可以打一个新标签:

代码语言:javascript
复制
$ git tag v1.0

可以用命令git tag查看所有标签:

代码语言:javascript
复制
$ git tag
v1.0

默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?

方法是找到历史提交的commit id,然后打上就可以了:

代码语言:javascript
复制
$ git log --pretty=oneline --abbrev-commit
12a631b (HEAD -> master, tag: v1.0, origin/master) merged bug fix 101
4c805e2 fix bug 101
e1e9c68 merge with no-ff
f52c633 add merge
cf810e4 conflict fixed
5dc6824 & simple
14096d0 AND simple
b17d20e branch test
d46f35e remove test.txt
b84166e add test.txt
519219b git tracks changes
e43a48b understand how stage works
1094adb append GPL
e475afc add distributed
eaadf4e wrote a readme file

比方说要对add merge这次提交打标签,它对应的commit id是f52c633,敲入命令:

代码语言:javascript
复制
$ git tag v0.9 f52c633

再用命令git tag查看标签:

代码语言:javascript
复制
$ git tag
v0.9
v1.0

注意,标签不是按时间顺序列出,而是按字母排序的。可以用git show <tagname>查看标签信息:

代码语言:javascript
复制
$ git show v0.9
commit f52c63349bc3c1593499807e5c8e972b82c8f286 (tag: v0.9)
Author: Michael Liao <askxuefeng@gmail.com>
Date:   Fri May 18 21:56:54 2018 +0800

    add merge

diff --git a/readme.txt b/readme.txt
...

还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:

代码语言:javascript
复制
$ git tag -a v0.1 -m "version 0.1 released" 1094adb

【注意:】标签总是和某个commit挂钩。如果这个commit既出现在master分支,又出现在dev分支,那么在这两个分支上都可以看到这个标签。

5.2 删除标签或者推送标签

如果标签打错了,也可以删除:

代码语言:javascript
复制
$ git tag -d v0.1
Deleted tag 'v0.1' (was f15b0dd)

因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。

如果要推送某个标签到远程,使用命令git push origin <tagname>:

代码语言:javascript
复制
$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
 * [new tag]         v1.0 -> v1.0

或者,一次性推送全部尚未推送到远程的本地标签:

代码语言:javascript
复制
$ git push origin --tags
Total 0 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
 * [new tag]         v0.9 -> v0.9

如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:

代码语言:javascript
复制
$ git tag -d v0.9
Deleted tag 'v0.9' (was f52c633)

然后,从远程删除。删除命令也是push,但是格式如下:

代码语言:javascript
复制
$ git push origin :refs/tags/v0.9
To github.com:michaelliao/learngit.git
 - [deleted]         v0.9

要看看是否真的从远程库删除了标签,可以登陆GitHub查看。

6,搭建Git服务器

远程仓库一节中,我们讲了远程仓库实际上和本地仓库没啥不同,纯粹为了7x24小时开机并交换大家的修改。

GitHub就是一个免费托管开源代码的远程仓库。但是对于某些视源代码如生命的商业公司来说,既不想公开源代码,又舍不得给GitHub交保护费,那就只能自己搭建一台Git服务器作为私有仓库使用。

搭建Git服务器需要准备一台运行Linux的机器,强烈推荐用Ubuntu或Debian,这样,通过几条简单的apt命令就可以完成安装。

假设你已经有sudo权限的用户账号,下面,正式开始安装。

第一步,安装git

代码语言:javascript
复制
$ sudo apt-get install git

第二步,创建一个git用户,用来运行git服务:

代码语言:javascript
复制
$ sudo adduser git

第三步,创建证书登录:

收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。

第四步,初始化Git仓库:

先选定一个目录作为Git仓库,假定是/srv/sample.git,在/srv目录下输入命令:

代码语言:javascript
复制
$ sudo git init --bare sample.git

Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾。然后,把owner改为git

代码语言:javascript
复制
$ sudo chown -R git:git sample.git

第五步,禁用shell登录:

出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑/etc/passwd文件完成。找到类似下面的一行:

代码语言:javascript
复制
git:x:1001:1001:,,,:/home/git:/bin/bash

改为:

代码语言:javascript
复制
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell

这样,git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。

第六步,克隆远程仓库:

现在,可以通过git clone命令克隆远程仓库了,在各自的电脑上运行:

代码语言:javascript
复制
$ git clone git@server:/srv/sample.git
Cloning into 'sample'...
warning: You appear to have cloned an empty repository.

剩下的推送就简单了。

管理公钥

如果团队很小,把每个人的公钥收集起来放到服务器的/home/git/.ssh/authorized_keys文件里就是可行的。如果团队有几百号人,就没法这么玩了,这时,可以用Gitosis来管理公钥。

这里我们不介绍怎么玩Gitosis了,几百号人的团队基本都在500强了,相信找个高水平的Linux管理员问题不大。

管理权限

有很多不但视源代码如生命,而且视员工为窃贼的公司,会在版本控制系统里设置一套完善的权限控制,每个人是否有读写权限会精确到每个分支甚至每个目录下。因为Git是为Linux源代码托管而开发的,所以Git也继承了开源社区的精神,不支持权限控制。不过,因为Git支持钩子(hook),所以,可以在服务器端编写一系列脚本来控制提交等操作,达到权限控制的目的。Gitolite就是这个工具。

这里我们也不介绍Gitolite了,不要把有限的生命浪费到权限斗争中。

7,参考

(1)廖雪峰的Git教程 https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.04.14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1,摘要
  • 2,GIT基本操作
    • 2.1 安装GIT
      • 2.2 创建版本库
        • 2.3 把文件添加到版本库
          • 2.4 仓库管理
          • 3.远程仓库(GitHub为例)
            • 3.1 连接远程仓库配置GitHub
              • 3.2 添加远程库
                • 3.3 从远程库克隆
                • 4. 分支管理
                  • 4.1 创建与合并分支概念
                    • 4.2 创建分支git checkout -b
                      • 4.3 查看分支git branch
                        • 4.4 合并分支git merge
                          • 4.5 删除分支git branch -d
                            • 4.6 解决冲突
                              • 4.7 分支管理策略
                                • 4.8 保存工作现场git stash
                                • 4.9 多人协作git push
                                • 5. 标签管理
                                  • 5.1 创建标签git tag
                                    • 5.2 删除标签或者推送标签
                                    • 6,搭建Git服务器
                                    • 7,参考
                                    相关产品与服务
                                    代码托管
                                    CODING 代码托管(CODING Code Repositories,CODING-CR)是为开发者打造的云端便捷代码管理工具,旨在为更多的开发者带去便捷、高效的开发体验,全面支持 Git/SVN 代码托管,包括代码评审、分支管理、超大仓库等功能。
                                    领券
                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档