Commit without add 以及如何查看远程分支日志

Commit without add and how to see remote branch log

1.I 是 git 的新手,想知道如果我有一个文件在过去被修改并且已经上演(但现在又被修改了),我会发生什么想要使用如下命令提交文件:
git commit -m "yada yada" ~/home/Dan/project/file_to_commit.py

这是否等同于:
a.git add ~/home/dan/project/file_to_commit.py
b.git commit -m "yada yada"

如果不是请解释。

2.How 我可以看到远程分支提交日志吗?(推送)而不执行 git 拉取?

谢谢

  1. [更新]

其实是等价的。当您使用 git commit <filepath> 直接提交文件时,它会在提交前暂存当前文件。使用 git add <file>.

提交文件之前,您必须在第一次添加文件时暂存文件(告诉存储库开始跟踪文件)

示例工作流程:

  • git add <file>
  • 做一些改变,yada yada
  • git commit -m "yada yada" <file>
  • 再做一些改变,等等等等
  • git commit -m "blah blah" <file>

2.

要查看远程 git 存储库的提交日志,首先在存储库上使用 git fetch,然后 运行 git log <path/branch> 查看日志。

看这里:https://github.com/abhikp/git-test/wiki/View-the-commit-log-of-a-remote-branch

作为两个单独的问题可能会更好,第二个问题已经得到正确回答,但我会尝试同时回答这两个问题。不过,在我开始之前,我想说路径 ~/home/Dan/project/file_to_commit.py 非常可疑:它表明您的 git 目录是 /.git,这不是一个好主意。我将在下面假设 .git 目录更靠下,您只需添加 project/filefile(我将 trim 路径在问题中)。

请注意,第一个答案的 TL;DR 版本是它们 几乎 相同,您只需要了解某些极端情况下的差异。 (因此 对于大多数用途来说可能已经足够了。)

Q1:添加并提交 vs git 提交 <em>path</em>

... I've a file which was modified and already staged in the past (but now modified again), and I want to commit the file using a command such as:

git commit -m "yada yada" file_to_commit.py

Is this equivalent to doing:

git add file_to_commit.py
git commit -m "yada yada"

If not please explain.

不,它不完全等价。这有点棘手,如果我们定义一些术语并进一步确定一些内容,将会有很大帮助。

此外,您说 "already staged in the past (but now modified again)",这会留下一些歧义:您是否在这些操作之间执行了 git commit?我将通过描述完整的一般案例来解决 "yes" 和 "no" 案例。

指数

首先,我们需要定义git的index或者暂存区(它还有几个名字,例如 cache,如 git diff --cached,但 "index" 和 "staging area" 是两个最常见的术语)。 Git 有一个(一个,单一的)standard 索引——我们称这个为 "the" 索引,当我们想引用另一个不同的索引时,我们将说出我们指的是哪个。当您 运行 大多数正常 git 命令时,包括 git add、git 更新此索引。1

接下来,我们需要记下 索引中的实际内容。除了一些有趣但不相关的情况,如合并,还有一件事我故意遗漏,本质上,索引每个文件都有一个条目,将在 next 提交中.2 也就是说,假设您已经提交或检查了某个分支,因此您的 current 提交现在在您的工作树,里面有 100 个文件。 (你的工作树可能有额外的 untracked and/or ignored 文件,只要它也有这 100 个文件。)

使用git add

当您 运行 git add 时,git 存储每个添加到存储库中的文件的新副本,它称为 blob objects。它在将每个 blob 添加到存储库时为每个 blob 计算哈希值,然后将新哈希放入索引中。

当你 运行 git commit——至少,没有路径或 -a——git 读取索引并使用它来形成新的提交。如果新提交与之前的提交具有相同的树,3 git 要求您添加 --allow-empty 标志。 (这并不意味着该索引是 empty,而是该索引与您通过 re-checking-out 和 current 提交。所以 --allow-empty 可能是这个标志的错误名称;它可能应该被称为 --allow-sameallow-unchanged 或类似的名称。)

因此,如果您 git 添加 <em>path</em> 然后 git commit -m <em>message</em>,你会得到一个使用 git add 更新的索引的提交,它可能有之前 [=26] 的其他更新=].但是,由于每个路径只有一个条目,如果您这样做:

... hack on foo.py ...
$ git add foo.py
$ echo '# add a final comment' >> foo.py
$ git add foo.py
$ git commit -m 'update foo'

在新的提交中只会对 foo.py 进行一次更新。

那有什么区别呢?

我在上面声明 git commit <em>path</em>(和 git commit -a)不是 完全 git add 然后 git commit 相同。让我们看看精确的区别。

当您为 git commit 提供路径名(或 -a)时,git 使用新的、不同的 临时索引 。它首先复制一些东西——确切地说是有点复杂的东西——到临时索引,然后它添加每个要提交的文件,然后从临时索引进行提交。提交完成后,git 返回使用正常的普通索引,并且它 更新索引 。也就是说,向临时索引添加内容并提交后,它也会添加到常规索引。

要了解这到底是如何工作的,我们需要一些例子。假设我们有两个文件,我们 git add 对其中一个文件进行更改:

# assume file1 and file2 are in the HEAD commit
echo add stuff to file1 >> file1
git add file1
echo add stuff to file2 too >> file2

在这一点上,git status 会告诉我们,我们对 file1 的更改已准备提交,对 file2 的更改 未提交 暂存待提交。

如果我们 运行 git add file2; git commit,我们将在新提交中获得两个更新。提交完成后,git status 将显示没有要提交的内容。但是,如果我们这样做:

git commit -m message file2

然后运行git status,我们会e file1 仍在上演 并准备提交。我们刚刚所做的提交 只有 更改为 file2.

这是因为 git commit file2 命令首先使用 HEAD 提交创建临时索引,添加 file2,进行提交,然后使用新索引更新正常索引file2。最后一点很重要:如果 git 没有将更改复制回(常规)索引,索引仍将具有 file2old 版本,下一次提交将撤消我们刚刚提交的更改。

这个copy-back步骤有一个重要的副作用。假设我们对 foo.py 进行了复杂的更改。例如,假设我们去修复一个错误,并在此过程中重构了一些功能。我们已经测试了修复并且它有效,所以我们做了 git add foo.py 并准备提交它:

... hack on foo.py ...
... test, hack more, test, until fixed ...
git add foo.py
git commit -m 'refactor foo.py and then fix a bug'^C

此时我们意识到我们不应该同时提交这两个更改:我们应该先提交重构代码 ,然后然后提交错误修复。4

好吧,我们已经 git add 重构了 和固定的 代码,所以它安全地隐藏在索引中,对吧? (不,错误!危险的 Will Robinson! 但是让我们继续,因为这是一个例子。)所以我们可以撤消修复部分,只留下重构,然后先提交:

... edit foo.py to remove just the fix ...
git commit -m 'refactor code to prep for bug fix' foo.py

完成提交后,我们可以提交暂存版本:

git commit -m 'fix bug #12345' foo.py

唉,git 现在告诉我们没有什么可提交的。 foo.py 精心设计的 full-fix 版本发生了什么?

答案是,git commit foo.py消灭了。 Git 首先将 refactor-only foo.py 添加到临时索引并提交;但随后它将 refactor-only foo.py 复制回常规索引,丢失了我们精心准备的 full-fix 版本。我们可以 re-create 它,或者在存储库中四处寻找因此留下的 "dangling blob"。

(这可能应该被认为是 git 中的错误,尽管不清楚如何处理暂存但未提交的版本。)

git commit -a and/or 路径:--only 对比 --include

这里我需要引用一下刚才的话。当使用 -a 或路径时,git commit 从复制 某些东西 开始——正是变得有点复杂 。如果仔细查看 the git commit documentation,您会发现 -i--include 选项(以及相应但默认的 -o / --only 选项)。这些控制进入临时索引的内容。

当使用 --include 时,git commit 从当前索引创建临时索引。使用默认的 --only 模式时,git commitHEAD 提交中创建临时索引。

(由于最后的 copy-back 步骤,要查看 both 命令实际上都在使用临时索引的唯一方法是查看索引提交操作的中间。如果我们使用 --include 并在提交完成后检查,常规索引将匹配新的 HEAD 提交,就好像 git commit 正在添加到常规索引而不是临时索引。幸运的是,通过不提供 -m 标志,可以很容易地查看常规索引 "in the middle",这样 git commit 就会启动编辑器。在进行时, 运行 git status 在另一个 window 中,或使用作业控制。这是一个示例:

# At this point I've modified both a.py and mxgroup.py
# but have not `git add`ed either one.

$ git add a.py
$ git status --short
M  a.py
 M mxgroup.py

# We see that a plain "git commit" would commit a.py,
# but not mxgroup.py.

$ git commit -i mxgroup.py
# now waiting in the editor

# Now, in another window:
$ git status -s
M  a.py
 M mxgroup.py

这表明(常规)索引仍然按照我们原来的方式设置。一旦我们写完消息并退出编辑器,提交过程将更新新 mxgroup.py 条目的常规索引,并且 now-committed a.py 更改也在新 HEAD 提交,因此 git status 将不显示任何文件。)

Q2:查看日志

How can I see the remotes branch commits logs?(pushes) without doing git pull?

Git 本身几乎完全在本地运行。您也许可以直接使用 Web 查看器执行此操作,但在本地执行会非常方便,首先 运行ning git fetch.

git pull命令实际上是为了方便。合并其他人的提交需要两个步骤:

  1. 获取这些提交,以便您在本地拥有它们;和
  2. 使用这些提交进行合并或变基。

这两个步骤由不同的 git 命令处理:git fetch 执行第 1 步,git mergegit rebase——您必须从两者中选择一个——执行您想要的第 2 步版本。

git pull 命令只是执行第 1 步,然后执行第 2 步。除非您另有指示,否则它会选择 merge。由于历史原因,它有多种方式选择第2步运行的操作。

我的建议是,作为 git 的新手,您应该完全避免使用 git pull。这有两个原因,其中只有一个在今天有效(除非你坚持使用非常旧的 git 版本):

  1. 传统上,git pull 有各种错误,其中一些甚至会丢失工作。 (据我所知,自 git 2.0 以来这些都是固定的。

  2. 虽然 方便,但 git pull 掩盖了正在发生的事情,让你过早选择 merge-vs-rebase。的确,rebase 几乎总是正确的答案,但是 git pull 默认是做 merge,这意味着它的默认值对于新用户来说是错误的。另外,当然还有 "obscures what's happening" 问题。如果你知道 fetch 然后 rebase 作为单独的步骤,这个问题可能甚至不会出现。


1索引只是一个文件,您可以在.git/index中找到它。您可以通过在环境中设置 GIT_INDEX_FILE 使 git 使用不同的索引,但这主要是为了供 git stash.

等脚本使用

2我遗漏的案例是:

  • 冲突合并,每个路径最多记录三个条目,使用non-zero 阶段编号 .一旦您解决了冲突并得到了 git add 结果,非零阶段将被清除并存储正常的 stage-0 结果,我们回到正常的 ready-to-commit 索引尝试的情况.

  • 正在删除当前提交中的文件(git rm,有或没有 --cached)。这将写入一个特殊的 stage-0 条目,将文件标记为 to-be-omitted,而不是简单地删除该条目。

3如果您提交合并,git 允许树匹配任何或所有 parents,因为合并提交需要记录倍数 parents。因此,"empty" 测试仅适用于 non-merge、single-parent 提交。这在现代 git 中的记录比在旧版本 git 中的记录要好得多,但它仍然有错误的 name.

4这与git本身无关。这里的想法是提交小的、可读的、可理解的,最重要的是 可测试 更改。任何时候你发现自己将提交写成 "do A and B, and fix C, and add D and E",这表明你可能应该将其拆分为每个事物的一个提交——在这种情况下,大约 5 个单独的提交。