Предположим, у меня есть ветвь b
, отслеживающая локальную ветвь master
.
Я пытаюсь написать сценарий для выбора всех коммитов b
как единого блока сверхулюбого дерева, на которое в данный момент master
указывает.
Поскольку это часть неинтерактивного скрипта, важно, чтобы вишня всегда удалась и никогда не возвращалась к интерактивному вводу.
Есть ли стратегия слияния или какая-то комбинация флагов, которые можно использовать, чтобы направлять git для непосредственного совершения конфликта слияния?
Я согласен с внесением поправок взафиксируйте факт, чтобы удалить конфликт слияния.
Суть этого в основном состоит в том, чтобы научиться писать сценарии git и лишь частично автоматизировать часть моего текущего рабочего процесса. Я знаю, что постоянно собирать вишню - это не The Git Way, и что я выбрасываю историю местного развития. Использование множества локальных веток, которые все отслеживают друг друга, также, по всей вероятности, не является Git Way.
Для целей этого вопроса, пожалуйста, рассмотрите историю в локальном хранилище, которая опрятна и аккуратна, когдарассматриваемый из внешнего мира как более важный, чем точная местная история .
Итак, вот пример ситуации, которую я пытаюсь разрешить.
создать каталог песочницы
$ mkdir -p /tmp/gitdir
перейти к каталогу песочницы
$ cd /tmp/gitdir
создать репозиторий git и ветку master
$ git init
записать файл, добавить в git,commit.
$ 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
дорожка b
из c
$ git branch -u b
добавить 2 коммита в ветку c
$ 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
является правильным в большинстве случаев, но иногда тянет за собой устаревшие коммиты. То, что я действительно пытаюсь сделать, это переместить содержимое ветки, которая рассматривается как patch или delta вперед на графике.
Приложение I
Вот моя попытка написать скрипт для автоматизации выбора вишни содержимого ветви поверх отслеживаемой ветви.
В настоящее время проблема заключается в том, что подпроцесс 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();