Table of Contents

1. 简介

2.基础命令

2.1. 仓库初始化

2.2. 查看仓库状态

    2.2.1. git status

    2.2.2. git log

    2.2.2. git show

    2.2.3. git diff

    2.2.4 git ls-files

2.3. 提交改变到仓库

2.4. 使用branch 更有效地工作

   2.4.1.merge

   2.4.2.Rebase

   2.4.3. 直接 rebase 服务器上的分支

   2.4.4. 使用rebase 合并多个commits.

2.5. 撤销,回退

    2.5.1. 撤销 ”工作目录” 下的改动

    2.5.2. 撤销 "index stage" 下的改动

    2.5.3. 撤销commit

    2.5.4. 回退到某个版本

2.6.提交到服务器

2.7.提取某个分支到本地

2.8. 输出/应用patch

3.Gerrit 使用

4.参考文献

 

1. 简介

 

简单说下,Git 与 Svn 的区别吧。Git 是一个分布式的源代码管理工具,它是 linux kernel的作者—— Linus Torvalds 开发用来管理 kernel源码,很快风靡开源社区。 与 Svn 类集中式的版本控制工具相比,它是分布式的—— 用户在工作的时候,不用依赖服务器,每个人的仓库都是一个服务器的副本,相互之间可以更新代码,不需要通过一个中央服务器。Git 功能强劲,可以用得非常复杂。 这篇文章只介绍一些最基本的用法。

Git 命令基本格式: git command -args -- args

git command --help 查看该命令的帮助。

 

2.基础命令

 

2.1. 仓库初始化

$ git clone path/to/repository

仓库根目录: `ls -la` 出现 ".git" 就是仓库的根目录。

所有git 命令,需要在仓库根目录及下级目录下运行。

 

2.2. 查看仓库状态

 

2.2.1. git status

现在编辑了三个文件 a.c , b.c d.c , 查看一下当前的工作状态。

$ git status

$ git status --untracked-files=no

 

Changes not staged for commit: 仓库里面的文件(README)被修改,修改没有添加到仓库.
Untracked files: a.c, b.c, d.c 这三个文件没有添加到仓库(untracked)。
如果出现乱码 , 可以尝试使用命令 : git config [--global] core.quotepath false

 

2.2.2. git log

$ git log —— 仓库的所有的commits

$ git log --stat —— 这个参数带来,更多的commits 信息:具体的文件改动。

$ git log -p —— 每个commit的文件改动, 信息量较大,可以使用 git log -p >git.log

$ git log -p file —— 查看某个文件历史改动信息

$ git log --no-merges

$ git log origin/[branch] --查看服务器仓库某个branch的日志

$ git log [commit id] —— 查看某个commit 的改动,而不是所有commits

$ git log -p 59e5a44bbb17b390928db109d8b5f0293cdf46f2

commit id 不用写全: $ git log -p 59e5a44bbb17b390928 也可以。

 

2.2.2. git show

查看commit 的具体改动,具体内容同diff 的格式。

git show [commit id]

git show [commit id] dir/file -- 查看某id下, 某个文件的改变。

git show branch file -- 查看某branch下, 某个文件的改变。

git show [commit id]:dir/fie -- 查看某id下,整个完整的文件。

 

2.2.3. git diff

git status 不同,git diff 输出差异的具体内容,而不是文件名。

git diff --stat

查看两个branch 某个目录下的差异

git diff branch_A branch_B dir1/dir2/dir

查看两个commit 间某个文件所有的改变

git diff HEAD HEAD~3 xx/xxx/xxxx.xml

 

2.2.4 git ls-files

查看工作区的所有文件,也就是所有被git 管理到文件名。

 

2.3. 提交改变到仓库

 

添加新文件: $ git add {files}.

提交文件修改: $ git add {files}.

提交: $ git commit -m

提交所有文件修改: $ git commit -a -m

修改(合并到)上一个commit : $ git commit --amend -m

如果只想提交 a.c b.c到仓库—— 因为还不确定是否需要 d.c git add a.c b.c .

提交针对README的修改 —— git add README

再次查看状态 $ git status



Changes to be commited: 文件将被作为一个commit 提交。
接下来,只需要 commit 就可以了, 但是 ——

还需要对git 进行一些基本配置。

$ git config user.email usr@email.com

$ git config user.name "Four Lee"

使用 --global 表明是全局的配置, 否则只是针对该仓库的配置。
git 在 用户目录 ~/.gitconfig 有一个全局的配置文件。在仓库的目录下有本地配置文件 .git/config 。可以直接修改相应配置。 git 先读取本地配置文件,然后再读取全局配置文件。
OK, 现在是提交的时候了。

$ git commit -m "Firt commit"

查看日志 $ git log

Author: 就是之前配置的 user.name 和 user.email。
commit 是一个hash 值,是一个独一无二的commit ID。

提交工作结束了,但是——

糟糕!提交信息写错了,应该是 “First commit”。 怎么办?

$ git commit --amend -m "First commit"

再次查看日志$ git log

 

Good!

开发继续进行进行,这次修改了a.c , b.c 和 README文件, 然后需要一次性全部提交:

$ git commit -a -m "Second commit"

如果提交信息较多需要换行, 可以不加 -m, git 会弹出文本编辑窗口,根据操作系统差异,有的是vim,有的是 GNU nano。 写入信息,保存即可!

每次提交后,推荐使用 git status 和 git log 来查看本次commit的状态,有问题及时使用 git commit --amend 进行修改。

 

2.4. 使用branch 更有效地工作

 

git 默认的主仓库是 master ,实际开发过程中,并不能总是延着master 一帆风顺地前进。

        

                                                                                                                          

到了commit m 的时候,你需要开发Feature A,可能难度很大,工作时间长,这中间会产生很多有效或无效的commits。这种情况,继续在 master上开发是不可能。

 

创建,切换到 feature_A

$ git branch feature_A

$ git checkout feature_A

 

查看当前branch

$ git branch

现在,你可以尽情地在 feature_A上工作,而不会影响到别人和自己的工作。如果,突然需要回到mster 去处理一个bug,而 feature_A上的工作尚未完成。
切换到 master

$ git checkout master

工作完成后,回到 branch feature_A 即可, 互相不受影响。
同svn 相比, git 的 branch 几乎没有额外系统开销,它不需要像svn那样,备份整个仓库。你应该总是在branch 上面工作,然后,合并到 master.

 

2.4.1.merge

$ git checkout master

$ git merge feature_A

但是——
开发的过程永远不会这么顺利

因为,你在开发feature_A 的时候,你的同事已经从 commit m, 推进到 commit n 了, 难免出现大家修改同一文件的情况。这种情况下,需要按照提示,手动合并一下冲突的文件。 不要轻易删除,修改人家的东西,万事好商量。 在本例中,a.c 发生了冲突。

提交冲突修改

$ git add a.c

$ git commit -m "Merge Feature A"

上图表明: 我们在 Second commit ,创建了 branch Feature_A, 当master 推进到 Third commit, 合并了 Feature A, 由于 a.c 文件冲突,又提交了一个新的 Merge Feature A commit. Feature A commit 插在了Third commit 和 Second commit 中间。然后,最上面是合并冲突的commit。

表面上是没什么问题,但是——
有一点不完美,那就是多了一个 Merge Feature A 的commit, 总觉得有点别扭。使用 rebase 可以解决这个问题。

 

2.4.2.Rebase

$ git rebase feature_A

同样,冲突等着你.

修改,合并

$ vim a.c

$ git add a.c

$ git rebase --continue

上面似乎一切都还好,可是——
如果,master上推进过多,我们的Feature A似乎都不容易被发现,有没有把 Feature A 放到最前面呢?
改变一下合并的工作流程。

更新master

$ git checkout master

$ git pull origin master

 

在branch Feature A里面,合并 master

$ git checkout feature_A

$ git rebase master

.. 合并冲突 ..

$ git rebase --continue

 

再次回到 master,把 Feature A并入

$ git merge feature_A

 

由于,在 branch feature_A已经处理了冲突,这次只是例行公事,不会再出现冲突。

 

Feature A已经跑到最上面了,整个流程非常清爽。

 

2.4.3. 直接 rebase 服务器上的分支

如果,我们没有建立branch, 直接在master上工作; 或者rebase, 后服务器又有了新的更新; 或者在某个branch上,想直接更新到最新仓库上面(master)。

$ git remote update

$ git rebase remotes/origin/master

$ git push xx

 

2.4.4. 使用rebase 合并多个commits.

在 feaure A 开发过程中,有时候为了方便调试,会提交很多粒度比较细的commits , 如:

commit 1 "work snapshot 1"
commit 2 "work snapshot 2"
......
commit n "work snapshot n"

当工作正常完成后,其实,只需要提交最后一个commit 即可!放一系列无效的commits到仓库里面,不是好的工作方式。

合并多个commits

$ git rebase -i HEAD~4

所有需要合并的commit前面,改成 squash, 保存,退出编辑器。

这个地方是新的 commit mesage,根据需要任意修改, 不需要的删除。

现在只剩下一个commit。 接下来,合并到master 即可。

 

2.5. 撤销,回退

 

git 同svn相比还有一个大的优势是,git 有不少吃后悔药的机会。 svn 一旦 commit 了以后,就再也没有修改了。无论怎样回退,错误的commit 将一直存在。
先理解git 工作区的一个概念。

(图片来自网络)

我们编辑文件后,改动在 ”工作目录” (working dir), 使用 git add 后,改动跑到 “Index stage”, git commit 后,保存到仓库里面。

 

2.5.1. 撤销 ”工作目录” 下的改动

$ git stash 或者 $ git reset --hard

推荐使用 git stah ,因为使用 reset --hard 后,再也找不回来了。而使用 stash后,还可以再后悔一次:

$ git stash apply stash@{0} —— 将恢复文件修改

 

2.5.2. 撤销 "index stage" 下的改动

git add后,发现 d.c ,我并不想添加进来。

$ git reset

可以看到,文件修改全部回到了 "工作目录",重新git add 即可。

 

2.5.3. 撤销commit

d.c 被错误地提交到了HEAD commit.

 

$ git reset HEAD^1

$ git status

改动又回退到了 "工作目录"

 

2.5.4. 回退到某个版本

发现自己走了弯路,或者整个开发都弄乱了,所有的改动都不想要了。

$ git reset --hard {commit id}

commit id 是需要回退到的版本的commit ID 。
慎重使用,使用 git reset --hard 是找不回来的。

 

2.6.提交到服务器

 

上面所有的工作都是在本地完成的,所有的commit都在本地的仓库。到一定阶段后,需要与服务同步一下:
从服务器master把更新拉下来

$ git pull origin master | git pull origin master:master

把本地master的更新提交上去

$ git push origin master

把本地master的提交到feature_A branch.

$ git push origin master:feature_A

还可以把 feature_A 推上去,供其它同事(张三)测试。

$ git push origin feature_A

张三可以在 master或者其它branch 上面,拉下feature_A的代码。

$ git pull origin feature_A

工作结束,删除服务器上的feature_A 分支

$ git push origin :feature_A

 

2.7.提取某个分支到本地

 

查看服务器地址

$ git remote -v

aosp ssh://xxx@xx (fetch) 
aosp ssh://xxx@xx (push)

 

查看服务器上的分支

$ git fetch
$ git branch -r

aosp/l-cmc-828 
m/l-cmc-828 -> aosp/l-cmc-828

 

提取到本地

git fetch aosp l-cmc-828:l-cmc-828, 如不成功
git fetch aosp aosp/l-cmc-828:l-cmc-828 —— 似乎同服务器配置有关。

 

切换到新的分支

git checkout l-cmc-828

 

2.8. 输出/应用patch

 

git format-patch -s HEAD^1 -- 生成当前HEADpatch 文件。

git format-patch -M origin/master -- 本地与服务器差异的patch文件。

git format-patch (-p) commit1..commitn --生成 commit 1 n 间所有的patch文件

git am [patch] -- 打上某个patch

如果,出现冲突,会产生(.rej files),解决掉冲突后,使用
git add, git am --continue,直到整个过程完成。

3.Gerrit 使用

 

安装 hook, commit 的时候生成 Change-Id.

$ gitdir=$(git rev-parse --git-dir);
$ scp -p -P 29418 帐号@服务器地址:hooks/commit-msg ${gitdir}/hooks/
如: scp -p -P 29418 usr@192.168.3.5:hooks/commit-msg ${gitdir}/hooks/
或者直接使用".git" 来代替 "$(gitdir)" :  scp -p -P 29418 usr@192.168.3.5:hooks/commit-msg  .git/hooks/

上传到服务器的临时区域

$ git push origin HEAD:refs/for/master

当前branch所有未提交的commit 都会被push.

 

$ git push origin <branch>:refs/for/<branch>
$ git push origin foo:refs/for/foo

提交 'foo' branch 所有未提交的 commits 到 'foo' branch 的临时区域,
该commit 最终合并到 'foo' branch.


$ git push origin work:refs/for/master

提交 'work' branch 所有未提交的 commits 到 'master' branch的临时区域,
该 commit 最终合并到 'master' branch

 

cherry-pick : 可以把gerrit上面的 commit 拉到本地
Gerrit 页面,点击 Downlod,复制 Cherry Pick的命令,运行即可。

对提交到gerrit上面的commit,如果本地已经删除,可以使用该命令找回。

 

$ git cherry-pick 5b4b9c63ba18fa9ac2ec68a002d1b26399953aa2

把commit 5b4b9c63ba18 拉到当前的branch.

 

Q: 如何修改提交到 gerrit 上面的old commit ?

A: 提交到gerrit 上面的commit ,会被打回,要求修改,可能是提交“很久”后,推荐的工作模式为:

1: 本地保持一个与服务器同步的主trunk, 如: master → master.
2: 本地的工作都在自己的branch上面,如: 'fix_xx'.

工作场景一:

本地服务器 master : a, b, c —— C 为 HEAD commit.

本地 fix_xx: a, b, c , d, e, f —— 其中 d,e,f 修正了某个问题,全部提交到了 gerrit,

还没有通过审核。现在 d 被打回,需要修改。

切换到master,并 更新,与服务器同步。

$ git checkout master
$ git pull origin master

cherry-pic 'd', 修改,重新提交。

$ git cherry-pick <d commit id>
$ git add ..
$ git commit –amend.
$ git push origin HEAD:refs/for/master

 

工作场景二:

本地的无工作branch. 全部工作都在 master上面,也就是:
本地master: a, b, c , d, e, f —— 其中 d,e,f 全部提交到了 gerrit,还没有通过审核。
这种情况下,需要先建立一个新的branch 与服务器同步,并更新。
$ git     checkout     -b     master-server
$ git     reset     –hard    <c commit id>
$ git     pull    origin    master

$ .. (后续操作同工作场景一)

通过同步服务器,cherry-pick可以依次对work branch上的各个commit进行修改,但是会造成两个branch上面的内容不一致。所以,不要一直在某个work branch上面长时间工作,相应的工作完成后,就同步master,再建立新的work branch。合理的工作流程是:

           work1 ---> commit 1 ---> commit 2 ---> commit n

                |

master --- > commit 1 ---> commit 2 ---> commit 3 ---> commit m ---> commit n

                                                               |

                                    work2 ---> commit 1 ---> commit 2

 

workx 全部合入 master后,就删除workx branch..

 

4.参考文献

 

Git User’s Manual : https://www.kernel.org/pub/software/scm/git/docs/user-manual.html

Gerrit 的手册文档

用户评论:
发表评论: (限500字)

注册 忘记密码 登录