Имейте удаленный репозиторий git, откажитесь от коммитов на push - PullRequest
9 голосов
/ 11 января 2010

Что я хочу сделать:

Я хочу запретить любые коммиты слияния , чтобы быть выдвинутыми в центральное хранилище. Единственное исключение состоит в том, что происходит слияние между ветвями, существующими в центральном хранилище. Я хочу применить это в центральном хранилище.

Объяснение, почему я хочу это сделать:

Примечание. Если это объяснение сбивает вас с пути того, что я хочу сделать, игнорируйте это объяснение. Конечно, я рад услышать другие способы решения проблемы, которую я объясняю ниже, но ответ, который меня интересует, заключается в том, что я хочу сделать, как указано выше.

У меня есть центральное хранилище git с веткой, которую отслеживают несколько разработчиков. У каждого разработчика есть удаленный компьютер, настроенный для ветки этого центрального репозитория.

Мы придерживаемся политики синхронного принятия для этого проекта, поэтому каждый разработчик должен всегда перебазировать свою последнюю работу поверх удаленной ветки HEAD, прежде чем нажать. Я хотел бы применить эту политику, запретив любые коммиты слияния, которые когда-либо будут отправлены в центральный репозиторий. Единственное исключение, если слияние происходит между ветвями, которые существуют в центральном хранилище.

Чтобы упростить, я не хочу, чтобы локальные ветви отслеживания разработчика когда-либо объединялись с удаленной веткой. Но лучше всегда делать перебаз в удаленной ветке.

Мы частично применили это на компьютере разработчика, установив значение branch.NAME.rebase = true, которое помогает избежать проблем, если разработчик использует git pull, однако нам нужно решение для принудительного применения этого на стороне центрального хранилища.

Самым базовым решением было бы отказаться от коммитов с комментарием: "Слияние ветки 'NAME' из GITURL", однако, что-то более подробное, если проверить, существуют ли все родители коммита в путях ветвления центрального репозитория, Дальше интереснее.

Предложения? Решения?

Edit:

Это то, что я имею до сих пор:

#!/bin/sh
read sha1old sha1new refname

# check if this is merge commit
merge_commit="`git rev-list --parents --merges --no-walk $sha1new 2> /dev/null`"
if test -n "$merge_commit"
then
  # this was a merge commit
  # $merge_commit contains: sha1new sha1parent_1 ... sha1parent_n
fi
exit 0

Место, которое вызывает беспокойство, определяет, происходит ли происхождение любых двух родителей от одной ветви. Кроме того, поскольку ловушка предварительного получения вызывается перед обновлением каких-либо ссылок, если push содержит фиксации для двух ветвей, существующих на удаленном компьютере, включая объединение между этими двумя ветвями, то я понятия не имею, какое решение будет здесь. .

1 Ответ

10 голосов
/ 17 января 2010

Один из способов предотвращения толчков, которые могли бы создать нелинейную историю, - это установить хук pre-receive, который использует git rev-list --parents <OLD>..<NEW>, чтобы проверить наличие любого нового коммита, имеющего более одного родителя, и завершиться с ошибкой, если это так.Чтобы справиться со вторым требованием, вы можете вместо этого проверить, что если имеется более одного родителя, все эти коммиты должны быть в существующих ветвях в хранилище.Я не очень много тестировал, но этот pre-receive хук (или какой-то его вариант) может быть тем, что вы хотите:

#!/usr/bin/ruby -w

# A pre-receive hook that should refuse any pushes that would update
# master in such a way that a non-linear history would be created,
# except where it involves a merge from another branch in this
# repository.  This has only had very cursory testing.

# This is a suggested answer to:
#   /1767405/imeite-udalennyi-repozitorii-git-otkazhites-ot-kommitov-na-push

ref_to_check = "refs/heads/master"

rev_old, rev_new, ref = STDIN.read.split(" ")

if ref == ref_to_check
  merge_bases = `git merge-base #{rev_old} #{rev_new}`.strip.split(/\s+/)
  unless $?.success? and merge_bases.length == 1
    STDERR.puts "No unique merge base found between #{rev_old} and #{rev_new}"
    exit(1)
  end
  rev_list_output = `git rev-list --parents #{merge_bases[0]}..#{rev_new}`
  list_of_revs_with_parents = rev_list_output.strip.split(/[\r\n]+/)
  list_of_revs_with_parents.each do |line|
    rev_with_parents = line.strip.split(/\s+/)
    if rev_with_parents.length > 2      
      parents = rev_with_parents.slice(1,rev_with_parents.length)
      # The question says to permit non-linear history if the merge is
      # from another branch in the central repository, so check
      # whether that's the case.  (If you just want to prevent all
      # pushes that add non-linear history, just exit with error
      # here.)
      any_parent_not_on_any_branch = false
      parents.each do |p|
        branches = `git branch --contains #{p} 2> /dev/null`
        if $?.success? and ! branches.strip.empty?
          STDERR.puts "More than one parent of commit #{rev_with_parents[0]}"
          STDERR.puts "... but parent #{p} is on branches:"
          STDERR.puts branches
        else
          STDERR.puts "Parent #{p} not found on any other"
          STDERR.puts "branch in this repository"
          any_parent_not_on_any_branch = true
          break
        end
      end
      if any_parent_not_on_any_branch
        STDERR.puts "Refusing push, since it would create non-linear history"
        STDERR.puts "for #{ref} and the merges don't just involve commits on"
        STDERR.puts "other branches in this repository."
        exit(2)
      end
    end
  end
end

Надеюсь, он вам пригодится.

...