Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/git/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用Git查找分支点?_Git_Branch - Fatal编程技术网

使用Git查找分支点?

使用Git查找分支点?,git,branch,Git,Branch,我有一个带有主分支和主分支的存储库,并且在这两者之间有很多合并活动。基于master创建分支A时,如何在存储库中找到提交 我的存储库基本上如下所示: -- X -- A -- B -- C -- D -- F (master) \ / \ / \ / \ / G -- H -- I -- J (branch A) 我正在寻找修订版A,它不是git merge base(--all)找到

我有一个带有主分支和主分支的存储库,并且在这两者之间有很多合并活动。基于master创建分支A时,如何在存储库中找到提交

我的存储库基本上如下所示:

-- X -- A -- B -- C -- D -- F  (master) 
          \     /   \     /
           \   /     \   /
             G -- H -- I -- J  (branch A)
我正在寻找修订版A,它不是
git merge base(--all)
找到的版本。

您可能正在寻找:

git merge base在三向合并中使用的两个提交之间找到最佳公共祖先。如果共同祖先是前者的祖先,那么一个共同祖先比另一个共同祖先更好。没有更好的共同祖先的共同祖先是最好的共同祖先,即合并基。请注意,一对提交可以有多个合并基


一般来说,这是不可能的。在分支历史记录中,分支和合并发生在命名分支被分支之前,而两个命名分支的中间分支看起来相同


在git中,分支只是历史部分提示的当前名称。他们没有很强的认同感

这通常不是什么大问题,因为两个提交的合并基础(参见Greg Hewgill的回答)通常更有用,提供两个分支共享的最新提交

依赖于提交的父级顺序的解决方案显然在分支在分支历史的某个时刻已完全集成的情况下不起作用

git commit --allow-empty -m root # actual branch commit
git checkout -b branch_A
git commit --allow-empty -m  "branch_A commit"
git checkout master
git commit --allow-empty -m "More work on master"
git merge -m "Merge branch_A into master" branch_A # identified as branch point
git checkout branch_A
git merge --ff-only master
git commit --allow-empty -m "More work on branch_A"
git checkout master
git commit --allow-empty -m "More work on master"
如果在父级反转的情况下进行集成合并,则该技术也会失败(例如,使用临时分支执行测试合并到主级,然后快进到功能分支以进一步构建)


您可以检查分支A的reflog,以查找它是从哪个commit创建的,以及该分支指向哪个commit的完整历史记录。Reflogs在
.git/logs中

git log --pretty=oneline master > 1
git log --pretty=oneline branch_A > 2

git rev-parse `diff 1 2 | tail -1 | cut -c 3-42`^

我用了
git rev list
来做这类事情。例如,(注意3个点)

将吐出分支点。现在,它并不完美;由于您已经将主控形状合并到分支A中好几次了,这将拆分出两个可能的分支点(基本上是原始分支点,然后是将主控形状合并到分支A中的每个点)。然而,它至少应该缩小可能性

我在
~/.gitconfig
中将该命令添加到我的别名中,如下所示:

[alias]
    diverges = !sh -c 'git rev-list --boundary $1...$2 | grep "^-" | cut -c2-'
所以我可以称之为:

$ git diverges branch-a master

我在寻找同样的东西,我发现了这个问题。谢谢你的邀请

然而,我发现我在这里看到的答案似乎并没有完全给出您要求的答案(或者我正在寻找的答案)——它们似乎给出了
G
commit,而不是
A
commit

因此,我创建了以下树(按时间顺序分配的字母),以便测试:

A - B - D - F - G   <- "master" branch (at G)
     \   \     /
      C - E --'     <- "topic" branch (still at E)
因此,的目标是:找到B。经过一番修补,我发现了三种方法:


1.使用gitk进行可视化: 您应该可以直观地看到这样一棵树(从master查看):

或此处(从主题查看):

在这两种情况下,我都选择了提交,它在我的图中是
B
。单击它后,它的完整SHA将显示在图形下方的文本输入字段中


2.视觉上,但从终端:
git日志--图形--单线--全部

(编辑/旁注:添加
--decoration
也很有趣;它会添加分支名称、标记等的指示。由于下面的输出不反映其用途,因此不会将其添加到上面的命令行。)

其中显示(假设git config--global color.ui auto
):

或者,直截了当地说:

* a9546a2 merge from topic back to master |\ | * 648ca35 merging master onto topic | |\ | * | 132ee2a first commit on topic branch * | | e7c863d commit on master after master was merged to topic | |/ |/| * | 37ad159 post-branch commit on master |/ * 6aafd7f second commit on master before branching * 4112403 initial commit on master 您还可以将其放入~/.gitconfig中(注意:尾随破折号很重要;感谢您提醒注意这一点):

从外壳:

git config --global alias.oldest-ancestor '!zsh -c '\''diff --old-line-format='' --new-line-format='' <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | head -1'\'' -'

祝你快乐

鉴于此线程中的许多答案没有给出问题所要求的答案,下面是每个解决方案的结果摘要,以及我用于复制问题中给出的存储库的脚本

日志 创建具有给定结构的存储库时,我们会得到以下git日志:

$ git --no-pager log --graph --oneline --all --decorate
* b80b645 (HEAD, branch_A) J - Work in branch_A branch
| *   3bd4054 (master) F - Merge branch_A into branch master
| |\  
| |/  
|/|   
* |   a06711b I - Merge master into branch_A
|\ \  
* | | bcad6a3 H - Work in branch_A
| | * b46632a D - Work in branch master
| |/  
| *   413851d C - Merge branch_A into branch master
| |\  
| |/  
|/|   
* | 6e343aa G - Work in branch_A
| * 89655bb B - Work in branch master
|/  
* 74c6405 (tag: branch_A_tag) A - Work in branch master
* 7a1c939 X - Work in branch master
我唯一添加的是标记,它明确了我们创建分支的点,从而明确了我们希望找到的提交

有效的解决方案 唯一有效的解决方案是正确返回
A

$ diff -u <(git rev-list --first-parent branch_A) \
          <(git rev-list --first-parent master) | \
      sed -ne 's/^ //p' | head -1
74c6405d17e319bd0c07c690ed876d65d89618d5
return
I提供的解决方案

$ git merge-base master branch_A
a06711b55cf7275e8c3c843748daaa0aa75aef54
$ git merge-base --all master branch_A
a06711b55cf7275e8c3c843748daaa0aa75aef54
返回
X
提供的解决方案:

$ diff -u <(git log --pretty=oneline branch_A) \
          <(git log --pretty=oneline master) | \
       tail -1 | cut -c 2-42
7a1c939ec325515acfccb79040b2e4e1c3e7bbe5
我怀疑git版本对此有多大不同,但是:

$ git --version
git version 1.7.1

感谢您向我展示了编写示例存储库脚本的更为简洁的方法。

您可以使用以下命令返回分支a中最早的提交,该分支a无法从master访问:

git rev-list branch_a ^master | tail -1

也许通过一个额外的健全性检查,确认提交的父级实际上可以从主级访问…

我相信我已经找到了一种处理这里提到的所有角落案例的方法:

branch=branch_A
merge=$(git rev-list --min-parents=2 --grep="Merge.*$branch" --all | tail -1)
git merge-base $merge^1 $merge^2
查尔斯·贝利(Charles Bailey)说得很对,基于祖先顺序的解决方案价值有限;在一天结束时,您需要某种“此提交来自分支X”的记录,但这样的记录已经存在;默认情况下,“git merge”将使用提交消息,如“merge branch”“branch_a”“into master”,这告诉您,来自第二个父级(commit ^2)的所有提交都来自“branch_a”,并合并到第一个父级(commit ^1),即“master”

有了这些信息,您可以找到“branch_A”(即“branch_A”真正存在的时候)的第一次合并,并找到合并基础,即分支点:)

我试过使用马克·布斯和查尔斯·贝利的存储库,解决方案奏效了;怎么不能呢?唯一不起作用的方法是手动更改合并的默认提交消息,以便
git config --global alias.branchdiff '!sh -c "git diff `git oldest-ancestor`.."'
git config --global alias.branchlog '!sh -c "git log `git oldest-ancestor`.."'
$ git --no-pager log --graph --oneline --all --decorate
* b80b645 (HEAD, branch_A) J - Work in branch_A branch
| *   3bd4054 (master) F - Merge branch_A into branch master
| |\  
| |/  
|/|   
* |   a06711b I - Merge master into branch_A
|\ \  
* | | bcad6a3 H - Work in branch_A
| | * b46632a D - Work in branch master
| |/  
| *   413851d C - Merge branch_A into branch master
| |\  
| |/  
|/|   
* | 6e343aa G - Work in branch_A
| * 89655bb B - Work in branch master
|/  
* 74c6405 (tag: branch_A_tag) A - Work in branch master
* 7a1c939 X - Work in branch master
$ diff -u <(git rev-list --first-parent branch_A) \
          <(git rev-list --first-parent master) | \
      sed -ne 's/^ //p' | head -1
74c6405d17e319bd0c07c690ed876d65d89618d5
$ git rev-list --boundary branch_A...master | grep ^- | cut -c2-
a06711b55cf7275e8c3c843748daaa0aa75aef54
413851dfecab2718a3692a4bba13b50b81e36afc
$ git merge-base master branch_A
a06711b55cf7275e8c3c843748daaa0aa75aef54
$ git merge-base --all master branch_A
a06711b55cf7275e8c3c843748daaa0aa75aef54
$ diff -u <(git log --pretty=oneline branch_A) \
          <(git log --pretty=oneline master) | \
       tail -1 | cut -c 2-42
7a1c939ec325515acfccb79040b2e4e1c3e7bbe5
mkdir $1
cd $1
git init
git commit --allow-empty -m "X - Work in branch master"
git commit --allow-empty -m "A - Work in branch master"
git branch branch_A
git tag branch_A_tag     -m "Tag branch point of branch_A"
git commit --allow-empty -m "B - Work in branch master"
git checkout branch_A
git commit --allow-empty -m "G - Work in branch_A"
git checkout master
git merge branch_A       -m "C - Merge branch_A into branch master"
git checkout branch_A
git commit --allow-empty -m "H - Work in branch_A"
git merge master         -m "I - Merge master into branch_A"
git checkout master
git commit --allow-empty -m "D - Work in branch master"
git merge branch_A       -m "F - Merge branch_A into branch master"
git checkout branch_A
git commit --allow-empty -m "J - Work in branch_A branch"
$ git --version
git version 1.7.1
git rev-list branch_a ^master | tail -1
branch=branch_A
merge=$(git rev-list --min-parents=2 --grep="Merge.*$branch" --all | tail -1)
git merge-base $merge^1 $merge^2
[alias]
    branch-point = !sh -c 'merge=$(git rev-list --min-parents=2 --grep="Merge.*$1" --all | tail -1) && git merge-base $merge^1 $merge^2'
find_merge ()
{
    local selection extra
    test "$2" && extra=" into $2"
    git rev-list --min-parents=2 --grep="Merge branch '$1'$extra" --topo-order ${3:---all} | tail -1
}

branch_point ()
{
    local first_merge second_merge merge
    first_merge=$(find_merge $1 "" "$1 $2")
    second_merge=$(find_merge $2 $1 $first_merge)
    merge=${second_merge:-$first_merge}

    if [ "$merge" ]; then
        git merge-base $merge^1 $merge^2
    else
        git merge-base $1 $2
    fi
}
#!/bin/bash
diff <( git rev-list "${1:-master}" --reverse --topo-order ) \
     <( git rev-list "${2:-HEAD}" --reverse --topo-order) \
--unified=1 | sed -ne 's/^ //p' | head -1
git rev-list $(git rev-list --first-parent ^branch_name master | tail -n1)^^! git rev-list --first-parent ^branch_name master git rev-list ^branch_name --first-parent master | tail -n1 git rev-list commit^^! git log -1 commit^ _C1___C2_______ master \ \_XXXXX_ branch A (the Xs denote arbitrary cross-overs between master and A) \_____/ branch B
git rev-list branch...master
git rev-list -1 `git rev-list branch...master | tail -1`^
git log --ancestry-path master..topicbranch
git checkout branch1    # refs/branch1 -> commit1
git checkout -b branch2 # branch2 -> commit1
After branch:
       C1 (B1)
      /
    -
      \
       C1 (B2)
After first commit:
       C1 (B1)
      /
    - 
      \
       C1 - C2 (B2)
After branch:
    - C1 (B1) (B2)
After first commit (human):
    - C1 (B1)
        \
         C2 (B2)
After first commit (real):
    - C1 (B1) - C2 (B2)
Either:
      / - C2 (B1)
    -- C1
      \ - C3 (B2)
Or:
      / - C3 (B1)
    -- C1
      \ - C2 (B2)
Human:
    - X - A - B - C - D - F (B1)
           \     / \     /
            G - H ----- I - J (B2)
Real:
            B ----- C - D - F (B1)
           /       / \     /
    - X - A       /   \   /
           \     /     \ /
            G - H ----- I - J (B2)
Paths:
    F->(D->(C->(B->(A->X)),(H->(G->(A->X))))),(I->(H->(G->(A->X))),(C->(B->(A->X)),(H->(G->(A->X)))))
    J->(I->(H->(G->(A->X))),(C->(B->(A->X)),(H->(G->(A->X)))))
    F->D,I | D->C | C->B,H | B->A | A->X | J->I | I->H,C | H->G | G->A
Paths simplified:
    F->(D->C),I | J->I | I->H,C | C->(B->A),H | H->(G->A) | A->X
Paths first parents only:
    F->(D->(C->(B->(A->X)))) | F->D->C->B->A->X
    J->(I->(H->(G->(A->X))) | J->I->H->G->A->X
Or:
    F->D->C | J->I | I->H | C->B->A | H->G->A | A->X
Paths first parents only simplified:
    F->D->C->B->A | J->I->->G->A | A->X
Topological:
    - X - A - B - C - D - F (B1)
           \
            G - H - I - J (B2)
      / - B (B1)
    - A
      \ - C (B2)
      / - B - D (B1)
    - A      /
      \ --- C (B2)
Expected:
      / - B - D (B1)
    - A      / \
      \ --- C - E (B2)
Reality:
      / - B - D (B1) (B2)
    - A      /
      \ --- C
From:
      / - B - D - E (B1)
    - A      /
      \ --- C (B2)
To:
      / - B - D - E (B1) (B2)
    - A      /
      \ --- C
         0   1   2   3   4 (B1)
        /-\ /-\ /-\ /-\ /
    ----   -   -   -   -
        \-/ \-/ \-/ \-/ \
         5   6   7   8   9 (B2)
$ git log --all --oneline --decorate --graph

*   a9546a2 (HEAD -> master, origin/master, origin/HEAD) merge from topic back to master
|\  
| *   648ca35 (origin/topic) merging master onto topic
| |\  
| * | 132ee2a first commit on topic branch
* | | e7c863d commit on master after master was merged to topic
| |/  
|/|   
* | 37ad159 post-branch commit on master
|/  
* 6aafd7f second commit on master before branching
* 4112403 initial commit on master
$ git log --all --oneline --decorate --graph --first-parent

* a9546a2 (HEAD -> master, origin/master, origin/HEAD) merge from topic back to master
| * 648ca35 (origin/topic) merging master onto topic
| * 132ee2a first commit on topic branch
* | e7c863d commit on master after master was merged to topic
* | 37ad159 post-branch commit on master
|/  
* 6aafd7f second commit on master before branching
* 4112403 initial commit on master
$ git log --decorate --oneline --graph --first-parent master origin/topic