创建版本库

初始化一个Git仓库,使用git init命令; 添加文件到Git仓库,分两步:

  • 使用使用命令git add <file>,注意,可反复多次使用,添加多个文件;
  • 第二步,使用命令git commit,完成。

example:

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

时光机穿梭

  • 查看当前仓库下工作区状态,使用git status命令。
  • 如果git status告诉你有文件被修改过,用git diff <file>可以查看修改内容。

版本回退

  • HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id。回退到上一个版本可以使用git reset --hard HEAD^,上上一个就是HEAD^^,再多了就不方便,通常使用commit_id来指定回退到哪个版本。
  • 穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。
  • 要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本

工作区和暂存区

工作区就是我们本地的文件,使用git add命令,每次将工作区里的修改添加到暂存区。每次git的机制是记录每次的修改,而非每个版本的内容,大大减少了存储量。然后git commit命令每次将暂存区里所有内容提交到当前分支里。

管理修改

如果不add到暂存区,没法commitbranch里去。 注意:

  • 第一次修改->git add->第二次修改->git commit-> branch里保存的是第一次修改内容
  • 第一次修改->git add->第二次修改->`git add->git commit-> branch里保存的是第二次修改内容;

上面的对比,形象的表达出,每做一次修改,都需要git add一下,也就是需要加到暂存区里,否则无法commit到分支里。

撤销修改

  • 场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- <file>
  • 场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD <file>,就回到了场景1,第二步按场景1操作。
  • 场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。

example:

$ git checkout -- readme.txt #场景1
$ git reset HEAD readme.txt #场景2
$ git checkout -- readme.txt

删除文件

使用命令 git rm <file>,然后还要git commit一下,告诉分支这文件被删除了。

远程仓库

使用命令$ ssh-keygen -t rsa -C "[email protected]"生成自己的私钥id_rsa和公钥id_rsa.pub,将公钥里的内容拷贝到Github里“Account settings”,“SSH Keys”页面,即可。 Github可以提交需要SSH key,这样你可以在家里提交,也可以在公司提交,每一个终端对应一个公钥(也就是SSH key)。

添加远程库

在Github上新建一个repo,命名为learngit,在本地的learngit仓库下运行

$ git remote add origin [email protected]:Ocxs/learngit.git
$ git remote add <主机名> <网址>

该命令是添加远程主机,其中origin是远程库的名字,Git默认的叫法。其中网址可以使用https协议,但是使用SSH支持的原生Git协议很快,且每次推送都不需要输入密码,首次推送有个警告,确定即可。

接下来将本地库的内容推送到远程库,使用命令

$ git push -u origin master

由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。以后直接使用git push origin master即可。

从远程库克隆

上面讲的是从本地推送repo到github上(本地到远程),下面用命令git clone克隆一个本地库(远程到本地) example:

$ git clone [email protected]:Ocxs/gitskills.git

分支管理

一个repo,有多个分支,然后多人协作,git的优点之一。

创建与合并分支

在git中,HEAD是一个指针,指向不同分支。 创建分支dev并且切换到dev分支:

$ git checkout -b dev

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

$ git branch dev # 创建分支
$ git checkout dev #切换到dev分支

然后使用git branch命令查看当前分支:

$ git branch
* dev
  master

当前分支前面会标一个*号。 然后在readme.txt里面加一些内容,然后使用git add readme.txtgit commit -m "branch test",然后使用git checkout master命令切换到master分支下面,这时候查看readme文件,是无法看到修改的,但是使用命令

$ git merge dev #将dev分支合并到当前分支

后,就会发现readme.txt文件发生变化,有了之前的添加内容。 删除分支的命令是:

$ git branch -d dev

小结:

  • 查看分支:git branch
  • 创建分支:git branch <name>
  • 切换分支:git checkout <name>
  • 创建+切换分支:git checkout -b <name>
  • 合并某分支到当前分支:git merge <name>
  • 删除分支:git branch -d <name>

解决冲突

当你在一个分支feature1里修改了内容,在master分支里也修改了内容,当两个内容不一样时,使用 git merge命令就会有冲突,这时候只能手动修改,改好之后再git addgit commit即可。如果没有冲突的话,git merge后不需要git addgit commit。可以使用

$ git log --graph --pretty=oneline --abbrev-commit

查看合并的情况。--pretty=oneline --abbrev-commit可以不加。

分支管理策略

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

$ git merge --no-ff -m "merge with no-ff" dev

其中--no-ff参数,表示禁用Fast forward,因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。合并后,我们用git log看看分支历史就能看到之前的dev分支。

总结:合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。

Bug分支

适用场景:当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还没有提交,并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?

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

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

git status查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。因为如果工作区不干净,在创建并转到其他分支的时候,会提示你让你先commit当前的内容,在转到其他分支。 当你创建bug分支,修复好bug,在切换回来后,如何恢复刚才的工作现场呢?

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

恢复有两个方法:

  • git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;
  • git stash pop,恢复的同时把stash内容也删了。

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

$ git stash apply stash@{0}

Feature分支

这一节主要讲如何强制删除一个分支,使用

$ git branch -D <name>

多人写作

查看远程库信息:

$ git remote
origin

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

$ git remote -v
origin  [email protected]:michaelliao/learngit.git (fetch)
origin  [email protected]:michaelliao/learngit.git (push)

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

推送分支:

$ git push origin dev # dev是分支的名字,可以是master等其他名字

抓取分支:

$ git clone [email protected]:michaelliao/learngit.git
# 该命令抓取的是master分支,如过需要抓取dev分支,在dev分支上开发,使用下面命令:
$ git checkout -b dev origin/dev

如果此时遇到之前说的合并冲突,则需要手动解决冲突,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送,如果git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置devorigin/dev的链接:

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

然后在git pull,在本地合并,解决冲突,再推送.

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

  • 首先,可以试图用git push origin branch-name推送自己的修改;
  • 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
  • 如果合并有冲突,则解决冲突,并在本地提交;
  • 没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功! 如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream branch-name origin/branch-name。 这就是多人协作的工作模式,一旦熟悉了,就非常简单。

总结:

  • 查看远程库信息,使用git remote -v
  • 本地新建的分支如果不推送到远程,对其他人就是不可见的;
  • 从本地推送分支,使用git push origin branch-name,如果推送失败,先用git pull抓取远程的新提交;
  • 在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致;
  • 建立本地分支和远程分支的关联,使用git branch --set-upstream branch-name origin/branch-name
  • 从远程抓取分支,使用git pull,如果有冲突,要先处理冲突。

标签管理

每个commit都会有个号,比如6a5819e...,这种很难记,所以需要搞个tag,按照tag就能找到与之匹配的commit,会方便许多。

创建标签

$ git tag v1.0

就给当前分支的最新commit打了一个tag为v1.0。由于默认标签是打在最新提交的commit上的,如果想给之前的commit打tag,则先找到历史提交的commit id,然后在打tag:

$ git log --pretty=oneline --abbrev-commit
6a5819e merged bug fix 101
cc17032 fix bug 101
7825a50 merge with no-ff
6224937 add merge
59bc1cb conflict fixed
400b400 & simple
75a857c AND simple
fec145a branch test
d17efd8 remove test.txt
...
# 然后给commit id为6224937的commit打tag
$ git tag v0.9 6224937

这时候使用命令git tag查看标签:

$ git tag
v0.9
v1.0

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

$ git show v0.9
commit 622493706ab447b6bb37e4e2a2f276a20fed2ab4
Author: Michael Liao <[email protected]>
Date:   Thu Aug 22 11:22:08 2013 +0800

    add merge
...

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

$ git tag -a v0.1 -m "version 0.1 released" 3628164

小结:

  • 命令git tag <name>用于新建一个标签,默认为HEAD,也可以指定一个commit id;
  • git tag -a <tagname> -m "blablabla..."可以指定标签信息;
  • git tag -s <tagname> -m "blablabla..."可以用PGP签名标签;(这个我感觉我暂时用不到, 就没介绍)
  • 命令git tag可以查看所有标签。

    操作标签

    如果标签打错了,也可以删除:
    $ git tag -d v0.1
    Deleted tag 'v0.1' (was e078af9)
    
    因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。

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

$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To [email protected]:michaelliao/learngit.git
 * [new tag]         v1.0 -> v1.0

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

$ git push origin --tags
Counting objects: 1, done.
Writing objects: 100% (1/1), 554 bytes, done.
Total 1 (delta 0), reused 0 (delta 0)
To [email protected]:michaelliao/learngit.git
 * [new tag]         v0.2 -> v0.2
 * [new tag]         v0.9 -> v0.9

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

$ git tag -d v0.9
Deleted tag 'v0.9' (was 6224937)

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

$ git push origin :refs/tags/v0.9
To [email protected]:michaelliao/learngit.git
 - [deleted]         v0.9

小结:

  • 命令git push origin <tagname>可以推送一个本地标签;
  • 命令git push origin --tags可以推送全部未推送过的本地标签;
  • 命令git tag -d <tagname>可以删除一个本地标签;
  • 命令git push origin :refs/tags/<tagname>可以删除一个远程标签。

使用Github

先Fork别人项目,然后在从自己的帐号上clone,用git clone,然后在git push,通过发pull request给别人,看别人是否接受。

自定义Git

配置颜色:

$ git config --global color.ui true

忽略特殊文件,配置.gitignore

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

解决办法:在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。 不需要从头写.gitignore文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore

example:

# 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,就完成了! 如果想添加一个文件到git,但是由于该文件被.gitignore忽略了,两种解决方法:

  • 强制添加,使用命令git add -f App.class;
  • 找出.gitignore哪个地方规则限制了该问题,然后修改,
      $ git check-ignore -v App.class
      .gitignore:3:*.class    App.class
    
    Git会告诉我们,.gitignore的第3行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。

配置别名

example:

$ git config --global alias.co checkout # git co == git checkout
$ git config --global alias.ci commit # git ci == git commit
$ git config --global alias.br branch # git br == git branch

配置文件:

$ cat .git/config 
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
    precomposeunicode = true
[remote "origin"]
    url = [email protected]:Ocxs/learngit.git
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
    remote = origin
    merge = refs/heads/master
[alias]
    last = log -1
    co = checkout
    ci = commit
    br = branch
    st = status

别名就在[alias]后面,要删除别名,直接把对应的行删掉即可。配置Git的时候,加上--global是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。每个仓库的Git配置文件都放在.git/config文件中。

搭建Git服务器

暂时用不到,不赘述。

results matching ""

    No results matching ""