如何从主文件合并后找出分支上的更改? [英] How to find out what changes on a branch after merges from master?

查看:102
本文介绍了如何从主文件合并后找出分支上的更改?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我为分支分叉 master 。我不时将 master 合并到我的分支中进行更新。

  /  -  B  -  d  -  G  - ħ -  J L  -   -  M  - > dev(HEAD)
/ /
----- A - C - E - F - I - K - > master

如何仅在我的分支上显示更改,排除合并中的更改?
I.e.仅在提交 B D H 时显示不同。 L M



git diff 不起作用,因为它包含从 master 的合并更改。



顺便说一句,如果有人知道快速找到 A 而不停止向下滚动日志,我将不胜感激。

解决方案

我不清楚你在找什么。但请注意:


  • 提交商店快照。这意味着commit K 有一个完整的源代码树,它独立于提交 A B C 等等。同样,提交 J 具有完整的快照,独立于 A K中的任何内容或任何其他提交。



    (这里的独立一词表示如果您要求Git检索commit J ,如果你在设置 J K >。它实际上并不是可能的来改变任何提交; git commit --amend 似乎改变提交,但实际上并没有。)

  • 在两个特定的提交中使用 git diff ,Git提取每个这两个快照,并比较这些快照。




简单差异



<因此, git diff KM 会告诉你 master (commit K )以及 dev (提交 M )的当前提示。


你也可以拼写这个 git diff master dev



这可能是你想看到的(再一次,这对我来说不是很清楚)。所以答案1: git diff master dev

多个独立差异



另一方面,也许你想要的是为commit B 显示一个diff,为commit <$ c提供一个diff $ c> D ,一个用于提交 H 的diff,一个用于提交 L 的diff,和一个差异提交 M 。也就是说,您希望每次看到每个非合并提交,与其单一父级进行比较。



您可以 git diff AB git diff BD ,依此类推。但你也可以只是 git show B git show D 等等。这表明你提交的是一个改变,而不是一个快照。



你可能想知道如果提交存储快照而不是改变,这是可能的。 (由于大多数其他版本控制系统实际上都存储了更改,因此这会使很多人出行。)这个矛盾的答案是 git show 查看你绘制的同一个图。



再次检查提交 B 。它面前有什么承诺?也就是说,哪个提交是在左边,按照左边的方向行?只有一个可能的祖先提交 B ,这是它的单亲,提交 A 。因此, git show B
$ b


  1. 提取快照commit ,然后

  2. 提取提交 B 的快照,然后

  3. 不同于这两个快照。

类似地,对于提交,只有一个立即祖先(父) M ,这是提交 L 。所以 git show M


  1. 提取然后

  2. 提取 M 的快照,然后

  3. 区分这两个快照。

如果这是您想要的,有趣的问题就变成:您如何找到 B D H L M 序列?这个答案有点复杂,但是关键命令是 git rev-list ,它与 git log 。这些命令( git log git rev-list 两者)的做法是行走提交图的。也就是说,你选择一些起点 - 在这种情况下,提交 M dev的提示 - 并告诉Git



问题在于,当您点击合并提交时,如提交 J ,Git会回到父母的全部。你想限制Git在你进行合并提交时只发现分支 dev 的提示。 Git有一个标志,拼写为 - first-parent 。这告诉 git rev-list 仅跟随每个合并提交的父第一个

我们也想跳过合并,所以我们可以添加 - 不合并(这不会影响后退过程,它只会限制打印的修订ID以排除合并)。



这导致答案2a: git rev-list --first-parent --no-merges ^ a dev (我们稍后会到达not A部分)。

现在,实际上使用这个 git rev-list 是一种痛苦,因为现在我们必须获取每个提交ID,然后运行 git show 在上面。不过,有一种更简单的方法,因为 c> git rev-list
git log 本质上是相同的命令 git log -p 将每个提交显示为补丁,与 git完全相同显示



这导致答案2b: git log -p --first-parent - 不合并^ A开发 。这里我们不一定需要 - no-merges ;

合并提交和组合差异



一个特殊的事情是 git显示不会, git diff 不会处理显示合并提交的情况,例如提交 J 。提交 J 具有两个父母,即提交 H K (顺便说一下,这意味着在提交 J :-)之前,您已经提交了 K )。如果运行 git diff HJ ,Git会为 H J提取快照, code>并比较它们。如果运行 git diff KJ ,Git会为 K J 并比较它们。但是如果你运行 git show J ,Git会:


  1. H ,然后
  2. 提取 K 的快照,然后

  3. 提取 J (合并)的快照,最后

  4. 产生Git调用的 combined diff

步骤4的组合差异试图以紧凑的方式显示 H K J 。在压缩变化时,Git 抛出任何文件,其中 H K 匹配 J 中的版本。



也就是说,假设文件 README.txt 有一些变化,从 H J 。但假设 README.txt K 相同 >Ĵ。换句话说,当您执行 git merge 时,您正在从 K 中获取更改,以使 J ,并且合并的另一端没有更改 README.txt 。这意味着 README.txt 与一个传入方完全匹配,因此组合的diff 完全忽略文件。 b

这意味着合并差异通常不会显示任何内容,即使合并在新快照中找到了一些更改。要查看这些更改,您必须制作两个差异,而不仅仅是一个。您必须从 H J 以及另一个来自 K J ,而不是依靠组合的差异。



当使用 git log -p ,您还可以通过添加 -c - cc 到选项。但是,如果你不这样做,那么Git所做的实际上是非常简单的:它根本不会显示任何差异。

b
$ b

因此,这会导致答案2c: git log -p --first-parent ^ A dev 。我们所做的就是放弃 - 不合并:我们现在将看到每个合并的日志消息,但不包含差异。



这是什么 ^ A 的东西?



这也与你的其他问题:


顺便说一句,如果有人知道快速找到A而不继续滚动日志,我将不胜感激。


答案是为commit A 符号名称 C>。找到它的ID一次,然后选择一个名称,如 dev master (但不要使用那些名称,因为那些)



这些都是 dev master git checkout 符号名称并在分支上结束。您也可以给 A 一个分支名称。你需要确保你没有 git checkout 这个分支并对它进行提交,因为如果你这样做了,你将成长一个新的分支,而不是离开指向提交 A



的分支名称或者,您可以使标记名指向提交 A 。这与分支名称几乎完全相同。这两个区别是:


  1. 这是一个标签名称:您不能将其作为分支检出,因此无法更改

  2. 这是一个标签名称:如果你 git push --tags ,你会将它作为标签发送到上游,然后其他人也会拥有它。

在这种情况下,第1点对您有利,第2点可能不是,所以这取决于你是否有利(不会意外地改变它)是值得冒险的(可能会意外地发布它)。

如果你的ID是 ,那么您可以:

  $ git标签A< id-of-commit- A> 

现在您拥有名称 A 。 (您可以稍后 git tag -d A 将其删除,但如果您意外发布了它,您可能会继续从上游获取它。)



返回 git log ^ A 字符串的问题>命令,所有 ^ A 都是告诉 git rev-list (因此 git log )到 stop 到达提交 A 时通过提交图形。提交也没有显示(对于 git rev-list ,未打印;对于 git log ,未显示日志输出)。前缀 ^ 符号是not的缩写,即让我可以从 dev 访问所有提交,但< em 可从 A 到达。添加 - first-parent 使得Git只遍历每个合并的父元素,这样我们就不会进入从<$ c合并的提交$ c $> master



^ a dev 语法也可以拼写 A..dev 。请注意,这适用于 git log git rev-list ,但对于 git diff 来说意味着非常不同。)


I forked master for a branch. From time to time I merge master into my branch for updates.

       /--B--D--G--H--J--L--M--> dev (HEAD)
      /        /     /
-----A--C--E--F--I--K---> master

How can I show the changes only on my branch, excluding those from the merges? I.e. show difference only on commits B, D, H, L, M.

git diff A doesn't work because it includes merged changes from master.

BTW, I will appreciate if anyone knows a quick way to find A without keep scrolling down the log.

解决方案

It's not clear to me precisely what you're looking for. But note that:

  • Commits store snapshots. This means that commit K has a complete source tree that is independent of whatever is in commits A, B, C, and so on. Likewise, commit J has a complete snapshot, independent of whatever is in A, or K, or any other commit.

    (The word "independent" here means that if you ask Git to retrieve commit J, it would not matter if you had somehow managed to alter commit K after making J. It's not actually possible to alter any commit, ever; git commit --amend seems to change a commit, but really does not.)

  • Using git diff on two specific commits, Git extracts each of the two snapshots, and compares those.

Simple diffs

Therefore, git diff K M will show you what is different between the current tip of master (commit K), and the current tip of dev (commit M).

You can also just spell this git diff master dev.

That may be what you want to see (again, it's not really clear to me). So answer 1: git diff master dev.

Multiple independent diffs

On the other hand, maybe what you want is to show one diff for commit B, one diff for commit D, one diff for commit H, one diff for commit L, and one diff for commit M. That is, you want to see each non-merge commit, one at a time, as compared to its (single) parent.

You could git diff A B, and git diff B D, and so on. But you can also just git show B, and git show D, and so on. This shows you the commit as a change, instead of as a snapshot.

You may be wondering how this is possible if commits store snapshots, rather than changes. (This trips a lot of people up since most other Version Control Systems actually do store changes.) The answer to this apparent contradiction is that git show looks up the same graph you drew.

Look again at commit B. What commit comes before it? That is, which commit is to its left, following lines leftwards? There's only one possible ancestor for commit B, and that's its single parent, commit A. So git show B:

  1. extracts the snapshot for commit A, then
  2. extracts the snapshot for commit B, then
  3. diffs those two snapshots.

Similarly, there's only one immediate ancestor (parent) for commit M, and that's commit L. So git show M:

  1. extracts the snapshot for L, then
  2. extracts the snapshot for M, then
  3. diffs those two snapshots.

If this is what you want, the interesting question becomes: how do you find the IDs of each commit in the B, D, H, L, and M sequence? The answer to this is a bit complicated, but the key command is git rev-list, which is essentially the same command as git log. What these commands (git log and git rev-list both) do is to walk the commit graph. That is, you pick some starting point—in this case, commit M, the tip of dev—and tell Git to walk backwards through the commits, looking at each commit's parents.

The problem is that when you hit a merge commit, such as commit J, Git walks back to all of its parents. You want to restrict Git to finding only the parent that was the tip of branch dev when you made the merge commit. Git has a flag for this, spelled --first-parent. This tells git rev-list to follow only the first parent of each merge commit.

We also want to skip the merges, so we can add --no-merges (this does not affect the walking-back process, it just limits the printed revision IDs to exclude the merges).

This leads to answer 2a: git rev-list --first-parent --no-merges ^A dev (we'll get to the "not A" part later).

Now, actually using this with git rev-list is kind of a pain, because now we have to take each commit ID and then run git show on it. There's a much easier way, though, because git rev-list and git log are essentially the same command, and git log -p shows each commit as a patch, doing much the same thing as git show.

This leads to answer 2b: git log -p --first-parent --no-merges ^A dev. We don't necessarily need the --no-merges here either; see below.

Merge commits and combined diffs

The one special thing that git show does, that git diff doesn't, is to handle the case of showing a merge commit, such as commit J. Commit J has two parents, namely commits H and K (incidentally, this implies that you made commit K before making commit J :-) ). If you run git diff H J, Git will extract the snapshots for H and J and compare them. If you run git diff K J, Git will extract the snapshots for K and J and compare them. But if you run git show J, Git will:

  1. extract the snapshot for H, then
  2. extract the snapshot for K, then
  3. extract the snapshot for J (the merge), and finally
  4. produce what Git calls a combined diff.

The combined diff from step 4 attempts to show, in a compact fashion, changes from both H and K to J. In compacting the changes, Git throws out any file where the version in either H or K matches the version in J.

That is, suppose file README.txt has some change from H to J. But suppose that README.txt is the same in K and J. In other words, when you did the git merge, you were picking up changes from K in order to make J, and there were no changes to README.txt from the other side of the merge. This means README.txt exactly matches one "incoming side", and hence the combined diff completely ignores the file.

This means combined diffs often show nothing at all, even though the merge picked up some change(s) in the new snapshot. To see those changes, you must make two diffs, not just one. You must make one diff from H to J, and another diff from K to J, rather than relying on the combined diff.

When using git log -p, you can also see combined diffs for merges, by adding -c or --cc to the options. But if you don't ask for this, what Git does is actually laughably simple: it just doesn't bother to show a diff at all.

So this leads to answer 2c: git log -p --first-parent ^A dev. All we did is drop the --no-merges: we will now see each merge's log message, but no diff.

What is this ^A thing?

This also ties into your other question:

BTW, I will appreciate if anyone knows a quick way to find A without keep scrolling down the log.

The answer to this is to make a symbolic name for commit A. Find its ID once and then choose a name, like dev or master (but don't use either those since those are in use!).

That's all dev and master are: they're just symbolic names for commits, plus the extra property that, as branch names, you can git checkout the symbolic name and wind up "on" the branch. You can give A a branch-name too. You will need to make sure you do not git checkout this branch and make commits on it, because if you do that, you'll grow a new branch, rather than just leaving the branch-name pointing to commit A.

Alternatively, you can make a tag-name pointing to commit A. That's almost exactly the same as a branch name. The two differences are:

  1. It's a tag name: you can't check it out as a branch, and therefore can't change it accidentally.
  2. It's a tag name: if you git push --tags you'll send it upstream as a tag, and then everyone else will have it too.

In this case point 1 is in your favor and point 2 is probably not, so it's up to you whether the advantage (can't accidentally change it) is worth the risk (might accidentally publish it).

If you have the ID of A, then, you can:

$ git tag A <id-of-commit-A>

and now you have the name A. (You can later git tag -d A to delete it, although if you accidentally published it, you'll probably keep getting it back from your upstream.)

Getting back to the question of the ^A string in the git log commands, all ^A does is tell git rev-list (and therefore git log) to stop walking through the commit graph upon reaching commit A. The commit is also not shown (for git rev-list, not printed; for git log, not shown in the log output). The prefix ^ symbol is short for "not", i.e., "get me all commits reachable from dev, but not reachable from A". Adding --first-parent makes Git traverse only the first parent of each merge, so that we do not walk into commits merged from master.

(The ^A dev syntax can also be spelled A..dev. Note that this works with both git log and git rev-list, but means something very different for git diff.)

这篇关于如何从主文件合并后找出分支上的更改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆