Июль 2012 обновления ( мерзавец 1.7.12 + )
теперь можно повторно основывать все фиксации, чтобы базироваться, и выбрать вторую фиксацию Y
, чтобы быть раздавленными с первым X
.
git rebase -i --root master
pick sha1 X
squash sha1 Y
pick sha1 Z
git rebase [-i] --root $tip
Эта команда может теперь использоваться для перезаписи всего продвижения истории от [1 111]"" вниз к корневой фиксации.
См. фиксация df5df20c1308f936ea542c86df1e9c6974168472 на GitHub от [1 125] Chris Webb (arachsys
) .
Исходный ответ (февраль 2009)
я полагаю, что Вы найдете различные рецепты для этого в ТАК вопрос" , Как я комбинирую первые две фиксации репозитория мерзавца? "
Charles Bailey обеспеченный там большинство подробный ответ , напоминая нам, что фиксация является полным деревом (не только diffs от предыдущие состояния).
И здесь старая фиксация ("начальная фиксация") и новая фиксация (результат сплющивания) не будет иметь никакого общего предка.
, Которые означают, Вы не можете" commit --amend
" начальная фиксация в новую, и затем повторно основывать на новую фиксацию начальной буквы историю предыдущей начальной фиксации (много конфликтов)
(Что последнее предложение больше не верно с [1 114])
Скорее (с [1 115] исходная "начальная фиксация", и B
, последующая фиксация должна была быть раздавлена в начальную):
Возвращаются к последней фиксации, что мы хотим сформировать начальную фиксацию (ГОЛОВА отсоединения):
git checkout
Сброс указатель ответвления на начальную фиксацию, но отъезд индекса и рабочего неповрежденного дерева:
git reset --soft
Исправление начальное дерево с помощью дерева от 'B':
git commit --amend
Временно тег эта новая начальная фиксация (или Вы могли помнить новую фиксацию sha1 вручную):
git tag tmp
Возвращаются к исходному ответвлению (примите ведущее устройство для этого примера):
git checkout master
Воспроизведение все фиксации после B на новую начальную фиксацию:
git rebase --onto tmp
Удаляют временный тег:
git tag -d tmp
, Что путь," rebase --onto
" не представляет конфликты во время слияния, так как это повторно основывает историю сделанный после последняя фиксация (B
), чтобы быть раздавленным в начальную (который был A
) к [1 120] (представление раздавленной новой начальной фиксации): тривиальная ускоренная перемотка вперед объединяется только.
, Что работы для" A-B
", но также и" A-...-...-...-B
" (любое количество фиксаций может быть раздавлено в начальную этот путь)
править: Немного фона
У нас есть аналогичная функция, которая отключена в 99% случаев; мы использовали такой подход, как:
public void DoSomething()
{
TraceCall(MethodBase.GetCurrentMethod().Name);
// Do Something
}
public void TraceCall(string methodName)
{
if (!loggingEnabled) { return; }
// Log...
}
TraceCall(MethodBase.GetCurrentMethod().Name)
Это было просто, но независимо от того, была ли включена трассировка или нет, мы понесли потери производительности из-за использования Reflection для поиска имени метода.
Наши варианты заключались в том, чтобы либо потребовать больше кода в каждый метод (и рисковать простыми ошибками или отказом) или переключиться на использование StackFrame
для определения метода вызова только , когда ведение журнала было включено.
Вариант A:
public void DoSomething()
{
if (loggingEnabled)
{
TraceCall(MethodBase.GetCurrentMethod().Name);
}
// Do Something
}
public void TraceCall(string methodName)
{
if (!loggingEnabled) { return; }
// Log...
}
Вариант B :
public void DoSomething()
{
TraceCall();
// Do Something
}
public void TraceCall()
{
if (!loggingEnabled) { return; }
StackFrame stackFrame = new StackFrame(1);
// Log...
}
Мы выбрали вариант Б. Он предлагает значительные улучшения производительности по сравнению с вариантом A , когда ведение журнала отключено, в 99% случаев и его очень просто реализовать.
Вот изменение кода Майкла , для отображения затрат / выгод этого подхода
using System;
using System.Diagnostics;
using System.Reflection;
namespace ConsoleApplication
{
class Program
{
static bool traceCalls;
static void Main(string[] args)
{
Stopwatch sw;
// warm up
for (int i = 0; i < 100000; i++)
{
TraceCall();
}
// call 100K times, tracing *disabled*, passing method name
sw = Stopwatch.StartNew();
traceCalls = false;
for (int i = 0; i < 100000; i++)
{
TraceCall(MethodBase.GetCurrentMethod());
}
sw.Stop();
Console.WriteLine("Tracing Disabled, passing Method Name: {0}ms"
, sw.ElapsedMilliseconds);
// call 100K times, tracing *enabled*, passing method name
sw = Stopwatch.StartNew();
traceCalls = true;
for (int i = 0; i < 100000; i++)
{
TraceCall(MethodBase.GetCurrentMethod());
}
sw.Stop();
Console.WriteLine("Tracing Enabled, passing Method Name: {0}ms"
, sw.ElapsedMilliseconds);
// call 100K times, tracing *disabled*, determining method name
sw = Stopwatch.StartNew();
traceCalls = false;
for (int i = 0; i < 100000; i++)
{
TraceCall();
}
Console.WriteLine("Tracing Disabled, looking up Method Name: {0}ms"
, sw.ElapsedMilliseconds);
// call 100K times, tracing *enabled*, determining method name
sw = Stopwatch.StartNew();
traceCalls = true;
for (int i = 0; i < 100000; i++)
{
TraceCall();
}
Console.WriteLine("Tracing Enabled, looking up Method Name: {0}ms"
, sw.ElapsedMilliseconds);
Console.ReadKey();
}
private static void TraceCall()
{
if (traceCalls)
{
StackFrame stackFrame = new StackFrame(1);
TraceCall(stackFrame.GetMethod().Name);
}
}
private static void TraceCall(MethodBase method)
{
if (traceCalls)
{
TraceCall(method.Name);
}
}
private static void TraceCall(string methodName)
{
// Write to log
}
}
}
Результаты:
Tracing Disabled, passing Method Name: 294ms
Tracing Enabled, passing Method Name: 298ms
Tracing Disabled, looking up Method Name: 0ms
Tracing Enabled, looking up Method Name: 1230ms
Быстрый и наивный тест показывает, что для кода, чувствительного к производительности, да, вы должны обратить внимание на это:
Не генерировать 100K кадров: 3 мс
Генерировать 100K кадров : 1805ms
На моей машине примерно 20 микросекунд на сгенерированный кадр. Небольшая, но ощутимая разница на большом количестве итераций.
Говоря о ваших последующих вопросах («Следует ли мне отключить генерацию StackFrame в моем приложении?»), Я бы посоветовал вам проанализировать ваше приложение, провести тесты производительности, например тот, который я сделал здесь, и посмотрите, соответствует ли разница в производительности вашей рабочей нагрузке.
using System;
using System.Diagnostics;
namespace ConsoleApplication
{
class Program
{
static bool generateFrame;
static void Main(string[] args)
{
Stopwatch sw;
// warm up
for (int i = 0; i < 100000; i++)
{
CallA();
}
// call 100K times; no stackframes
sw = Stopwatch.StartNew();
for (int i = 0; i < 100000; i++)
{
CallA();
}
sw.Stop();
Console.WriteLine("Don't generate 100K frames: {0}ms"
, sw.ElapsedMilliseconds);
// call 100K times; generate stackframes
generateFrame = true;
sw = Stopwatch.StartNew();
for (int i = 0; i < 100000; i++)
{
CallA();
}
Console.WriteLine("Generate 100K frames: {0}ms"
, sw.ElapsedMilliseconds);
Console.ReadKey();
}
private static void CallA()
{
CallB();
}
private static void CallB()
{
CallC();
}
private static void CallC()
{
if (generateFrame)
{
StackFrame stackFrame = new StackFrame(1);
}
}
}
}
Судя по документации MSDN, StackFrames создается постоянно:
StackFrame создается и передается дальше the call stack for every function call made during the execution of a thread. The stack frame always includes MethodBase information, and optionally includes file name, line number, and column number information.
The constructor new StackFrame(1)
you'd call would do this:
private void BuildStackFrame(int skipFrames, bool fNeedFileInfo)
{
StackFrameHelper sfh = new StackFrameHelper(fNeedFileInfo, null);
StackTrace.GetStackFramesInternal(sfh, 0, null);
int numberOfFrames = sfh.GetNumberOfFrames();
skipFrames += StackTrace.CalculateFramesToSkip(sfh, numberOfFrames);
if ((numberOfFrames - skipFrames) > 0)
{
this.method = sfh.GetMethodBase(skipFrames);
this.offset = sfh.GetOffset(skipFrames);
this.ILOffset = sfh.GetILOffset(skipFrames);
if (fNeedFileInfo)
{
this.strFileName = sfh.GetFilename(skipFrames);
this.iLineNumber = sfh.GetLineNumber(skipFrames);
this.iColumnNumber = sfh.GetColumnNumber(skipFrames);
}
}
}
GetStackFramesInternal
is an external method. CalculateFramesToSkip
has a loop that operates exactly once, since you specified only 1 frame. Everything else looks pretty quick.
Have you tried measuring how long it would take to create, say, 1 million of them?
Я рассматриваю возможность использования чего-то вроде StackFrame stackFrame = new StackFrame (1) для регистрации выполняемого метода
Из интереса: Почему? Если вам нужен только текущий метод, то
string methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;
кажется лучше. Может быть, не более производительный (я не сравнивал, но Reflection показывает, что GetCurrentMethod () не просто создает StackFrame, но творит некоторую «магию»), но более ясен в своем намерении.