我现在与Git一起工作了大约两年,但仅用于我的个人项目以及GitHub上的那些项目。 在工作中,我们仍然使用TFS和SVN(到目前为止)。 最近, Paolo Perrotta来我们公司举办了有关敏捷计划的课程,并且由于Git对我的大多数同伴来说还很陌生,因此他还Swift地在重构的背景下对Git进行了解释。 我真的很喜欢他的解释方法,这就是为什么我想在这里重复他的解释。
就在我们开始之前。
Git与其他VCS(版本控制系统)有何不同? 可能最明显的区别是Git是分布式的(例如,不同于SVN或TFS)。 这个
意味着,您将拥有一个本地存储库,该存储库位于一个名为.git
的特殊文件夹中,并且您通常(但不一定)拥有一个远程中央存储库,不同的协作者可以在该中央存储库中贡献其代码。 请注意,每个贡献者在其本地工作站上都具有该存储库的精确克隆 。
Git本身可以想象为位于文件系统顶部并处理文件的东西。 更好的是,您可以将Git想象成一个树结构,其中每次提交都会在该树中创建一个新节点 。 实际上,几乎所有的Git命令都可以在该树上导航并进行相应的操作。 因此,在本教程中,我想通过从构建的树的角度查看Git存储库来了解Git的工作方式。 为此,我将介绍一些常见的用例,例如
- 添加/修改新文件
- 创建和合并有合并冲突和没有合并冲突的分支
- 查看历史/变更日志
- 对特定提交执行回滚
- 将代码共享/同步到远程/中央存储库
术语
这是git术语:
- master –存储库的主分支。 根据工作流程,是一个人进行工作还是一个集成发生的人
- clone –通常将现有的git存储库从某个远程位置复制到本地环境。
- commit –将文件提交到存储库(本地); 在其他VCS中,通常称为“签到”
- 提取或拉取–类似于其他VCS中的“更新”或“获取最新”。 提取与拉取之间的区别在于,拉取将两者结合在一起,从远程存储库中提取最新代码并执行合并。
- push –用于将代码提交到远程存储库
- 远程–这些是存储库的“远程”位置,通常在某些中央服务器上。
- SHA – Git树中的每个提交或节点均由唯一的SHA密钥标识。 您可以在各种命令中使用它们,以操作特定的节点。
- head –是对我们存储库当前工作空间所指向的节点的引用。
- branch –就像在其他VCS中一样,区别在于Git中的分支实际上没有比给定节点上的特定标签更特别的地方。 它不是其他流行的VCS中文件的物理副本。
工作站设置
我不想讨论设置工作站的详细信息,因为有很多工具在不同平台上会有所不同。 对于这篇文章,我将在命令行上执行所有操作。 即使您不是shell专家,也应该尝试一下(它永远不会受伤)。 要设置命令行Git访问,只需转到git-scm.com/downloads ,您将在其中找到所需的操作系统下载。 也可以在这里找到更详细的信息。 一切设置完成后,您在PATH环境变量中添加了“ git”,然后要做的第一件事就是使用名称和电子邮件配置git:
$ git config --global user.name "Juri Strumpflohner"
$ git config --global user.email "myemail@gmail.com"
让我们开始吧:创建一个新的Git存储库
在开始之前,让我们创建一个新目录,git存储库将在该目录中生活并cd
进入该目录:
$ mkdir mygitrepo
$ cd mygitrepo
现在我们准备初始化一个全新的git仓库。
$ git init
Initialized empty Git repository in c:/projects/mystuff/temprepos/mygitrepo/.git/
我们可以通过使用以下命令检查git存储库的当前状态:
$ git status
# On branch master
#
# Initial commit
#
nothing to commit (create/copy files and use "git add" to track)
创建并提交新文件
下一步是创建一个新文件并向其中添加一些内容。
$ touch hallo.txt
$ echo Hello, world! > hallo.txt
再次,检查状态现在显示以下内容
$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# hallo.txt
nothing added to commit but untracked files present (use "git add" to track)
为了“注册”提交文件,我们需要使用以下命令将其添加到git中:
$ git add hallo.txt
现在检查状态表明文件已准备好提交:
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: hallo.txt
#
现在我们可以将其提交到存储库
$ git commit -m "Add my first file"
1 file changed, 1 insertion(+)
create mode 100644 hallo.txt
通常在提交消息中使用“状态”。 因此,与其编写“添加我的第一个文件”,不如编写“添加我的第一个文件”。 因此,如果我们现在退后一步,看看那棵树,我们将得到以下内容。
“标签” 主 节点指向一个节点 。
添加另一个文件
让我们添加另一个文件:
$ echo "Hi, I'm another file" > anotherfile.txt
$ git add .
$ git commit -m "add another file with some other content"
1 file changed, 1 insertion(+)
create mode 100644 anotherfile.txt
顺便说一句,请注意,这次我使用git add .
它将所有文件添加到当前目录( .
)。 从树的角度来看,我们现在有另一个节点,而主节点已移至该节点。
创建一个(功能)分支
分支和合并是使Git如此强大以及对其进行了优化的一个方面,它是一个分布式版本控制系统(VCS)。 确实, 功能分支非常流行与Git一起使用。 为要添加到系统中的每种新功能创建功能分支,并且通常在功能合并回主集成分支(通常是主分支)后将其删除。 这样做的好处是,您可以在单独的,隔离的“游乐场”中试用新功能,并在需要时快速地来回切换到原始的“ master”分支。 而且,可以通过简单地删除特征分支轻松地再次丢弃它(如果不需要的话)。 但是,让我们开始吧。 首先,我创建新功能分支:
$ git branch my-feature-branch
执行中
$ git branch
* master
my-feature-branch
我们得到了分支机构列表。 master
前面的*
表示我们当前在该分支上。 让我们改用my-feature-branch
:
$ git checkout my-feature-branch
Switched to branch 'my-feature-branch'
再次
$ git branch
master
* my-feature-branch
注意,您可以直接使用命令git checkout -b my-feature-branch
branch在一个步骤中创建和签出新分支。 与其他VCS的不同之处在于,只有一个工作目录 。 您所有的分支都位于同一个分支中,并且您创建的每个分支没有单独的文件夹。 相反,当您在分支之间切换时,Git会替换您的工作目录的内容,以反映您要切换到的分支中的目录。 让我们修改现有文件之一
$ echo "Hi" >> hallo.txt
$ cat hallo.txt
Hello, world!
Hi
…然后将其提交到我们的新分支
$ git commit -a -m "modify file adding hi"
2fa266a] modify file adding hi
1 file changed, 1 insertion(+)
注意 ,这次我使用git commit -a -m
一步添加和提交修改。 这仅适用于之前已添加到git repo的文件。 新文件将不会以这种方式git add
,并且需要像以前看到的那样显式git add
。 那我们的树呢?
到目前为止,一切似乎都很正常,我们在树上仍然有一条直线,但是请注意,现在master
保留在原处,我们以my-feature-branch
向前移动。 让我们切换回master并在那里修改相同的文件。
$ git checkout master
Switched to branch 'master'
如预期的那样, hallo.txt
未被修改:
$ cat hallo.txt
Hello, world!
让我们也将其更改并提交给master(这将在以后产生很好的冲突 )。
$ echo "Hi I was changed in master" >> hallo.txt
$ git commit -a -m "add line on hallo.txt"
c8616db] add line on hallo.txt
1 file changed, 1 insertion(+)
现在,我们的树使分支可视化:
合并和解决冲突
下一步是将功能分支合并回master
。 这是通过使用merge命令完成的
$ git merge my-feature-branch
Auto-merging hallo.txt
CONFLICT (content): Merge conflict in hallo.txt
Automatic merge failed; fix conflicts and then commit the result.
不出所料,我们在hallo.txt
存在合并冲突。
Hello, world!
<<<<<<< HEAD
Hi I was changed in master
=======
Hi
>>>>>>> my-feature-branch
让我们解决它:
Hello, world!
Hi I was changed in master
Hi
..然后提交
$ git commit -a -m "resolve merge conflicts"
[master 6834fb2] resolve merge conflicts
树反映了我们的合并。
跳到某个提交
让我们假设我们想跳回给定的提交。 我们可以使用git log
命令获取唯一标识树中每个节点的所有SHA标识符。
$ git log
commit 6834fb2b38d4ed12f5486ebcb6c1699fe9039e8e
Merge: c8616db 2fa266a
Author: = <juri.strumpflohner@gmail.com>
Date: Mon Apr 22 23:19:32 2013 +0200
resolve merge conflicts
commit c8616db8097e926c64bfcac4a09306839b008dc6
Author: Juri <juri.strumpflohner@gmail.com>
Date: Mon Apr 22 09:39:57 2013 +0200
add line on hallo.txt
commit 2fa266aaaa61c51bd77334516139597a727d4af1
Author: Juri <juri.strumpflohner@gmail.com>
Date: Mon Apr 22 09:24:00 2013 +0200
modify file adding hi
commit 03883808a04a268309b9b9f5c7ace651fc4f3f4b
Author: Juri <juri.strumpflohner@gmail.com>
Date: Mon Apr 22 09:13:49 2013 +0200
add another file with some other content
commit aad15dea687e46e9104db55103919d21e9be8916
Author: Juri <juri.strumpflohner@gmail.com>
Date: Mon Apr 22 08:58:51 2013 +0200
Add my first file
取一个标识符(如果不是全部,也没关系),然后使用checkout
命令跳转到该节点
$ git checkout c8616db
Note: checking out 'c8616db'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b new_branch_name
HEAD is now at c8616db... add line on hallo.txt
注意注释git打印出来。 这意味着什么? 分离的头意味着“头”不再指向分支“标签”,而是指向树中的特定提交。 您可以将HEAD视为“当前分支”。 当您使用git checkout
切换分支时,HEAD版本将更改为指向新分支的尖端。 …HEAD可能引用与分支名称不相关的特定修订版。 这种情况称为“ 分离的HEAD” 。 计算器后基本上当我现在改变hallo.txt并且提交了修改,树如下所示:
如您所见,新创建的节点上没有标签。
当前指向它的唯一参考是head
。
但是,如果现在再次切换到master,则先前的提交将丢失,因为我们无法跳回到该树节点。
$ git checkout master
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
576bcb8 change file undoing previous changes
If you want to keep them by creating a new branch, this may be a good time
to do so with:
git branch new_branch_name 576bcb8239e0ef49d3a6d5a227ff2d1eb73eee55
Switched to branch 'master'
实际上,git非常善于提醒我们这一事实。 该树现在再次如图6所示。
回滚
回跳是好的,但如果我们要关注的分支合并前撤销一切恢复到什么状态? 就像
$ git reset --hard c8616db
HEAD is now at c8616db add line on hallo.txt
这里的通用语法是git reset --hard <tag/branch/commit id>
。
共享/同步您的存储库
最终,我们通常希望通过将代码同步到中央存储库来共享我们的代码。 为此,我们必须添加一个remote 。
$ git remote add origin git@github.com:juristr/intro.js.git
要查看我是否成功,只需键入:
$ git remote -v
列出所有已添加的遥控器。 现在,我们需要将本地分支母版发布到远程存储库。 这样完成
$ git push -u origin master
我们完成了。 真正强大的功能是您可以添加多个不同的遥控器。 它通常与云托管解决方案结合使用,以在服务器上部署代码。 例如,您可以添加一个名为“ deploy”的远程,该远程指向一些云托管服务器存储库,例如
$ git remote add deploy git@somecloudserver.com:juristr/myproject
然后只要您要发布分支,就执行一个
$ git push deploy
克隆
同样,如果您想从现有的远程存储库开始,它也可以工作。 要做的第一步是“签出”源代码,在Git术语中称为克隆 。 所以我们会做类似的事情
$ git clone git@github.com:juristr/intro.js.git
Cloning into 'intro.js'...
remote: Counting objects: 430, done.
remote: Compressing objects: 100% (293/293), done.
remote: Total 430 (delta 184), reused 363 (delta 128)
Receiving objects: 100% (430/430), 419.70 KiB | 102 KiB/s, done.
Resolving deltas: 100% (184/184), done.
这将创建一个名为“ intro.js”的文件夹(在这种情况下),如果我们输入它
$ cd intro.js/
并检查远程服务器,我们发现远程仓库的跟踪信息已经设置好了
$ git remote -v
origin git@github.com:juristr/intro.js.git (fetch)
origin git@github.com:juristr/intro.js.git (push)
现在,我们可以正常开始启动提交/分支/推送周期。
资源和链接
上面的场景很简单,但同时可能也是最常用的场景。 但是Git的功能还很多。 要获取更多详细信息,您可能需要参考下面的链接。
- http://gitready.com/
- 图书:Scott Chacon撰写的Pro Git
- 15分钟内尝试Git
- GitHub的Scott Chacon介绍Git
- 我的个人Git备忘单,我不断添加要记住的内容
翻译自: https://www.javacodegeeks.com/2013/05/git-explained-for-beginners.html