Git 吉特;琐碎的;直接提交合并冲突的合并策略

Git 吉特;琐碎的;直接提交合并冲突的合并策略,git,Git,假设我有一个分支b跟踪本地分支master 我正试图编写一个脚本,以将b的所有提交作为一个单元,在master当前指向的任何树的顶部进行挑选 因为这是非交互式脚本的一部分,所以cherry pick始终成功并且永远不会返回到交互式输入,这一点很重要 是否有一种合并策略或一些标志组合可以用来指示git直接提交合并冲突 我可以在事后修改commit以删除合并冲突 这主要是为了学习如何编写git脚本,而只是部分地自动化我当前工作流程的一部分。我知道不断地采摘樱桃不是吉特的方式,我正在抛弃当地的发展历

假设我有一个分支
b
跟踪本地分支
master

我正试图编写一个脚本,以将
b
的所有提交作为一个单元,在
master
当前指向的任何树的顶部进行挑选

因为这是非交互式脚本的一部分,所以cherry pick始终成功并且永远不会返回到交互式输入,这一点很重要

是否有一种合并策略或一些标志组合可以用来指示git直接提交合并冲突

我可以在事后修改commit以删除合并冲突


这主要是为了学习如何编写git脚本,而只是部分地自动化我当前工作流程的一部分。我知道不断地采摘樱桃不是吉特的方式,我正在抛弃当地的发展历史。使用大量相互跟踪的本地分支也不是Git的方式

为解决这个问题,请考虑在本地仓库中的一个历史,它是整洁的,从外面看的时候比一个准确的地方历史更重要。
这是我试图解决的一个例子

创建沙盒目录

$ mkdir -p /tmp/gitdir
$ cd /tmp/gitdir
导航到沙盒目录

$ mkdir -p /tmp/gitdir
$ cd /tmp/gitdir
创建git回购和主分支

$ git init
写入文件,添加到git,提交

$ echo master > foo.txt`
$ git add foo.txt`
$ git commit -m 'user commit 1'`
[master (root-commit) e9bcb91] user commit 1
1 file changed, 1 insertion(+)
create mode 100644 foo.txt
创建新分支机构
b

$ git checkout -b b
Switched to a new branch 'b'
更改
foo.txt的内容并提交

$ echo b1 > foo.txt
$ git add -u
$ git commit -m 'user commit 2'
b
设置为跟踪主机

$ git branch -u master
创建分支
c

$ git checkout -b c
$ git branch -u b
$ echo c1 > foo.txt
$ git add -u
$ git commit -m 'user commit 3'
[c 04da4ab] user commit 3
1 file changed, 1 insertion(+), 1 deletion(-)
$ echo c2 > foo.txt
$ git add -u > foo.txt
$ git commit -m 'user commit 4'
[c 17df476] user commit 4
1 file changed, 1 insertion(+), 1 deletion(-)
c

$ git checkout -b c
$ git branch -u b
$ echo c1 > foo.txt
$ git add -u
$ git commit -m 'user commit 3'
[c 04da4ab] user commit 3
1 file changed, 1 insertion(+), 1 deletion(-)
$ echo c2 > foo.txt
$ git add -u > foo.txt
$ git commit -m 'user commit 4'
[c 17df476] user commit 4
1 file changed, 1 insertion(+), 1 deletion(-)
将2个提交添加到分支
c

$ git checkout -b c
$ git branch -u b
$ echo c1 > foo.txt
$ git add -u
$ git commit -m 'user commit 3'
[c 04da4ab] user commit 3
1 file changed, 1 insertion(+), 1 deletion(-)
$ echo c2 > foo.txt
$ git add -u > foo.txt
$ git commit -m 'user commit 4'
[c 17df476] user commit 4
1 file changed, 1 insertion(+), 1 deletion(-)
返回到
b
,并添加提交

$ git checkout b
Switched to branch 'b'
Your branch is ahead of 'master' by 1 commit.
  (use "git push" to publish your local commits)

$ echo b2 > foo.txt
$ git add -u
$ git commit -m 'user commit 5'
[b 30f68fa] user commit 5
 1 file changed, 1 insertion(+), 1 deletion(-)
返回分支
c

$ git checkout c
Switched to branch 'c'
Your branch and 'b' have diverged,
and have 2 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
因此,关于如何修复这种情况,我们有两种选择

大多数时候,在这种情况下,我想做的是在一个分支中的所有更改之后直接移动另一个分支中的更改

在这种情况下,
rebase
在大多数情况下都是正确的,但有时会引入过时的提交。我真正想做的是移动分支的内容,在图中被认为是一个补丁或增量


附录一 下面是我尝试编写的一个脚本,用于自动拾取正在跟踪的分支顶部的分支内容

当前的问题是,
git cherry pick
子进程有时会因为合并冲突而放弃,我希望它只提交冲突的文件

<强>请考虑此脚本是各种工作标记的证明。对剧本本身的反馈虽然受到赞赏,但并不是问题的重点。这里的脚本主要是作为我试图做什么以及为什么要做的具体“证据”。

#!/usr/bin/env perl

use strict;
use warnings;
use Carp;
use Data::Dumper;

use vars qw[*CHERRY_PICK_SINK];

BEGIN {
    $Carp::Verbose = 1;
}

# accepts: command string default command interpreter
# returns: lines of output with $/ stripped, error status
sub capture_lines {
    local ${^CHILD_ERROR_NATIVE};
    my ($cmd) = @_;
    croak if ref $cmd;
    my @o = `$cmd`;
    chomp foreach @o;
    return [@o], ${^CHILD_ERROR_NATIVE};
}

# accepts: ()
# returns: UUID, error
sub get_uuid {
    my $err;
    my $cmd = q[python -c 'import uuid; print(str(uuid.uuid4()))'];
    my $lines;
    ($lines, $err) = capture_lines($cmd);
    return undef, $err if $err;
    if (@$lines <= 0) {
        return [undef, 'empty output'];
    }
    my $line = $lines->[0];
    return $line, undef;
}

# accepts: ()
# returns: aref of hashes for current branch, error status
sub current_branch_hashes {
    my $cmd = q[git log --format="%H" '@{upstream}..HEAD'];
    my ($name, $err) = capture_lines($cmd);
    return $name, $err;
}

# accepts: ()
# returns: name of current branch
sub current_branch_name {
    my $cmd = q[git rev-parse --abbrev-ref --symbolic-full-name HEAD];
    my ($lines, $err) = capture_lines($cmd);
    my $name = $lines->[0];
    return $name, $err;
}

# accepts: ()
# returns: name of upstream, error status
sub current_branch_upstream_name {
    my $cmd = q[git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}'];
    my ($lines, $err) = capture_lines($cmd);
    my $name = $lines->[0];
    return $name, $err;
}

# accepts: committish (be careful)
# returns: hash, error code
sub rev_parse {
    my ($name) = @_;
    croak if ref $name;
    my $name_quoted = quotemeta($name);
    my $cmd = "git rev-parse ${name_quoted}";
    my ($lines, $err) = capture_lines($cmd);
    return $lines->[0], $err;
}

# accepts: branch_name, committish
# returns: error code
sub assign_branch {
    my ($key, $value) = @_;
    croak if ref $key;
    croak if ref $value;
    my $key_quoted = quotemeta($key);
    my $value_quoted = quotemeta($value);
    my $cmd = "git branch -f $key_quoted $value_quoted";
    my (undef, $err) = capture_lines($cmd);
    return $err;
}

# accepts: branch_name
# returns: error code
sub delete_branch {
    my ($key) = @_;
    croak if ref $key;
    my $key_quoted = quotemeta($key);
    my $cmd = "git branch -D ${key_quoted}";
    my $err;
    (undef, $err) = capture_lines($cmd);
    return $err;
}

# accepts: label1, label2
# returns: error status
# note: swaps the where the branch labels point to
sub swap_branch_labels {
    my ($label1, $label2) = @_;
    croak if ref $label1;
    croak if ref $label2;
    my ($hash1, $hash2, $err);
    ($hash1, $err) = rev_parse($label1);
    return $err if $err;
    ($hash2, $err) = rev_parse($label2);
    return $err if $err;
    $err = assign_branch($label1, $hash2);
    return $err if $err;
    $err = assign_branch($label2, $hash1);
    return $err if $err;
}

# accepts: committish
# returns: error status
sub checkout_old {
    my ($name) = @_;
    my $name_quoted = quotemeta($name);
    my $cmd = "git checkout ${name_quoted}";
    (undef, my $err) = capture_lines($cmd);
    return $err;
}

# accepts: name
# returns: error status
sub checkout_new {
    my ($name) = @_;
    my $name_quoted = quotemeta($name);
    my $cmd = "git checkout -b ${name_quoted}";
    (undef, my $err) = capture_lines($cmd);
    return $err;
}

# accepts: aref of commit hashes
# returns: exit status
sub cherrypick_aref {
    local *CHERRY_PICK_SINK;
    local ${^CHILD_ERROR_NATIVE};
    my ($hashes) = @_;
    my $cmd = 'git cherry-pick --stdin';
    open CHERRY_PICK_SINK, '|-', $cmd;
    for my $item (@$hashes) {
        chomp($item);
        print CHERRY_PICK_SINK "$item\n";
    }
    close CHERRY_PICK_SINK;
    return ${^CHILD_ERROR_NATIVE};
}

# accepts: ()
# returns: error
sub cherrypick_self {
    my ($hashes, $err) = current_branch_hashes();
    return "current_branch_hashes: $err" if $err;
    return "cherrypick_self: empty hashes" unless @$hashes >= 1;
    my $current_branch;
    ($current_branch, $err) = current_branch_name();
    return "current_branch_name: $err" if $err;
    my $temp_branch;
    ($temp_branch, $err) = get_uuid();
    return "get_uuid: $err" if $err;
    my $upstream;
    ($upstream, $err) = current_branch_upstream_name();
    return "current_branch_upstream_name: $err" if $err;
    $err = checkout_old($upstream);
    return "checkout_old: $err" if $err;
    $err = checkout_new($temp_branch);
    return "checkout_new: $err" if $err;
    $err = cherrypick_aref($hashes);
    return "cherry-pick: $err" if $err;
    $err = swap_branch_labels($temp_branch, $current_branch);
    return "swap branch labels: $err" if $err;
    $err = delete_branch($temp_branch);
    return "delete branch: $err" if $err;
}

cherrypick_self();
#/usr/bin/env perl
严格使用;
使用警告;
使用鲤鱼;
使用数据::转储程序;
使用vars qw[*CHERRY_PICK_SINK];
开始{
$Carp::Verbose=1;
}
#接受:命令字符串默认命令解释器
#返回:带$/stripped的输出行,错误状态
子捕捉线{
本地${^CHILD\u ERROR\u NATIVE};
我的($cmd)=@;
如果参考$cmd,则发出咯咯声;
我的@o=`$cmd`;
chomp foreach@o;
返回[@o],${^CHILD\u ERROR\u NATIVE};
}
#接受:()
#返回:UUID,error
子对象{
我的$err;
my$cmd=q[python-c'import-uuid;print(str(uuid.uuid4())');
我的$line;
($lines,$err)=捕获线($cmd);
如果$err,则返回undef$err;
如果(@$行[0];
返回$line,未定义;
}
#接受:()
#返回:当前分支哈希的aref,错误状态
子电流分支散列{
my$cmd=q[git log--format=“%H”@{upstream}..HEAD'];
我的($name,$err)=捕捉线($cmd);
返回$name,$err;
}
#接受:()
#返回:当前分支的名称
次级电流分支机构名称{
my$cmd=q[git rev parse--abbrev ref--symbolic full name HEAD];
我的($lines,$err)=捕捉线($cmd);
我的$name=$lines->[0];
返回$name,$err;
}
#接受:()
#返回:上游的名称,错误状态
子电流\分支\上游\名称{
my$cmd=q[git rev parse--abbrev ref--symbolic全名'@{upstream}'];
我的($lines,$err)=捕捉线($cmd);
我的$name=$lines->[0];
返回$name,$err;
}
#接受:承诺(小心)
#返回:散列,错误代码
子版本解析{
我的($name)=@;
如果引用$name,则发出咯咯声;
我的$name_quote=quotemeta($name);
my$cmd=“git rev parse${name\u quoted}”;
我的($lines,$err)=捕捉线($cmd);
返回$line->[0],$err;
}
#接受:分支机构名称,commitish
#返回:错误代码
支行{
我的($key,$value)=@;
如果ref$key,则发出咯咯声;
如果参考值为$value,则发出咯咯声;
我的$key报价=quotemeta($key);
我的$value报价=quotemeta($value);
my$cmd=“git branch-f$key\u quoted$value\u quoted”;
my(未定义,$err)=捕获线($cmd);
返回$err;
}
#接受:分支机构名称
#返回:错误代码
支部{
我的($key)=@;
如果ref$key,则发出咯咯声;
我的$key报价=quotemeta($key);
my$cmd=“git branch-D${key\u quoted}”;
我的$err;
(未定义,$err)=捕获线($cmd);
返回$err;
}
#接受:label1,label2
#返回:错误状态
#注意:交换分支标签指向的位置
子交换分支标签{
我的($label1,$label2)=@;
如果参考$label1,则发出咯咯声;
如果参考$label2,则发出咯咯声;
我的($hash1,$hash2,$err);
($hash1,$err)=rev_parse($label1);
如果$err,则返回$err;
($hash2,$err)=rev_parse($label2);
如果$err,则返回$err;
$err=assign_分支($label1,$hash2);
如果$err,则返回$err;
$err=assign_分支($label2,$hash1);
如果$err,则返回$err;
}
#接受:承诺
#返回:错误状态
分检{
我的($name)=@;
我的$name_quote=quotemeta($name);
my$cmd=“git checkout${name\u quoted}”;
(未定义,my$err)=捕获行($cmd);
返回$err;
}
#接受:名称
#