Записи по тегу git

Анастасия

git fatal: confused by unstable object source data

Ошибка. git fatal: confused by unstable object source data.

читать дальше

Теги: git
Анастасия

Скрипт на perl'e для заливки сайта из git-репозитория

Вашему вниманию релиз-скрипт. При первом запуске клонирует репозиторий, делает checkout и подготавливает архивы файлов и бд для заливки сайта на сервер. При последующих запусках заливает изменения из коммитов, которые есть в мастерской ветке с последнего успешно залитого коммита.

#!perl
# -d:ptkdb
# -T

use UploadConfig qw($opt);

my $file        = "./params";
my $params2save = {};
my $dir         = "./" . $opt->{project};
my @files2upload;

use lib './lib';
use strict;
use warnings;

#use Git::Class;
use Git::Repository;
use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
use Net::FTP;
use Data::Dumper;
$Data::Dumper::Terse = 1;
use File::Path qw(rmtree);

Upload();

sub Upload {
	my $filestoupload = getProject();

	# parse files before upload
	dir_walk( $dir, \&parseFile );
	unless ( 0 + @$filestoupload ) {
		createArchives();
	}
	else {
		ftpUpload($filestoupload);
	}
	writeLastCommit();
}

sub getProject {
	my ( $git, $worktree, @files );

	#clean
	rmtree( $opt->{uploadfilesdir} );

	my $lastcommit = getLastCommit();

	#$lastcommit=1;
	unless ($lastcommit) {
		rmtree( "./" . $opt->{project} );

		#       #clones
		Git::Repository->run( clone => $opt->{repo} => $dir );
		$git = Git::Repository->new( work_tree => $dir );
		$git->run( checkout => "master" );
	}
	else {

		#pull
		$git = Git::Repository->new( work_tree => $dir );
		$git->run( pull => $opt->{repo}, "master" );

		#@files =$git->run("diff --name-only ".$lastcommit);
		my $cmd = $git->command(
			diff => '--diff-filter=ACM',
			'--name-only', $lastcommit
		);
		my $log = $cmd->stdout;
		while (<$log>) {
			chomp;
			push @files, $_;
			print $_. "\n";
		}
		#$cmd->close();
	}
#	die "123";

	# Get Last Commit
	my $out;
	my $cmd = $git->command( log => '--pretty=oneline', '-1' );
	my $log = $cmd->stdout;
	while (<$log>) {
		$out .= $_;
	}
#	$cmd->close;
	if ( $out =~ /(\S+)/mi ) {
		$params2save->{lastCommit} = $1;
	}
	return \@files;
}

sub getLastCommit {
	local $/ = undef;
	open my $fh, "<", $file or return undef;
	my $content = <$fh>;
	close $fh;

	#return eval { $content };
	return $content;
}

sub writeLastCommit {
	open my $fh, ">", $file or die $!;

	#print $fh Dumper( $params2save->{lastCommit} );
	print $fh $params2save->{lastCommit};
	close $fh;
}

sub deleteAllFilesExept {
	my @files = @{ $_[0] };
}

sub parseFile {
	my $file      = shift;
	my $shortfile = $file;
	$shortfile =~ s{.*/}{};

	return unless ( $opt->{toChange}->{$shortfile} );
	my %toChange = %{ $opt->{toChange}->{$shortfile} };

	if ( -f $file ) {
		print "Converting: $file\n";
		local $/ = undef;
		open my $fh, "<", $file or die $!;
		my $content = <$fh>;
		close $fh;

		while ( my ( $old, $new ) = each %toChange ) {
			if ( $content =~ /$old/ ) {
				$content =~ s/$old/$new/si;
			}
		}
		open $fh, ">", $file or die "Can't open file $file: $!";
		print $fh $content;
		close $fh;
	}
}

sub createArchives {
	my @arc;
	mkdir $opt->{uploadfilesdir};

	#cgi
	foreach my $arch ( keys %{ $opt->{zipfiles} } ) {
		my $zip = Archive::Zip->new();

		# my $member = $zip->addDirectory('/');
		my $member;
		my @files;

		if ( ref $opt->{zipfiles}->{$arch} eq "ARRAY" ) {
			@files = @{ $opt->{zipfiles}->{$arch} };
		}
		else {
			push @files, $opt->{zipfiles}->{$arch};
		}

		foreach my $fn (@files) {
			dir_walk(
				$opt->{project} . "/" . $fn,
				sub {
					my $file = shift;
					$file =~ s/\.\///;
					my $zipfile = $file;
					$zipfile =~ s/^[A-Za-z0-9- ]+\///;
					my $shortfile = $file;
					$shortfile =~ s{.*/}{};

					my @dirs = split( /\//, $file );
					return if ( $fn eq "." and 0 + @dirs > 2 );

					unless (
						is_in_array( \@dirs, $opt->{toChange}->{excludeFiles} )
					  )
					{

						$zip->addFile( $file, $zipfile );
						print "/$zipfile zipped from $file \n";
					}
				}
			);
		}

		die 'ZIP write error'
		  unless $zip->writeToFileNamed( $opt->{uploadfilesdir} . "/" . $arch )
			  == AZ_OK;
		print "ready $arch\n";
		push @arc, $opt->{uploadfilesdir} . "/" . $arch;
	}
	return \@arc;
}

sub ftpUpload {
	my $files = shift;

	my $ftp = Net::FTP->new( $opt->{ftp}->{server}, Debug => 0, Passive => 1 )
	  or die "Cannot connect to some.host.name: $@";

	$ftp->login( $opt->{ftp}->{login}, $opt->{ftp}->{password} )
	  or die "Cannot login ", $ftp->message;
	$ftp->binary();

	print "\n\n uploading files: \n\n";

	foreach my $fn (@$files) {
		next if ( is_in_array( $fn, $opt->{toChange}->{excludeFiles} ) );

		my $source = $opt->{project} . "/" . $fn;
		print "uploading $source\n";
		if ( $fn =~ /cgi-bin/ ) {
			$fn =~ s/cgi-bin\///;
			my $uploadfn = $opt->{ftp}->{put}->{cgi} . $fn;
			$uploadfn =~ /(.*\/)(.*?)(\.[^.]*)/g;
			my $dir  = $1;
			my $dist = $2 . $3;
			$ftp->cwd($dir);

			$ftp->put( $source, $dist )
			  or die "Cannot put $source to $opt->{ftp}->{put}->{cgi}.$fn ",
			  $ftp->message;
		}
		else {
			my $uploadfn = $opt->{ftp}->{put}->{www} . $fn;
			$uploadfn =~ /(.*\/)(.*?)(\.[^.]*)/g;
			my $dir  = $1;
			my $dist = $2 . $3;
			print $dir," ", $dist;
			$ftp->cwd($dir);

			$ftp->put( $source, $dist )
			  or die "Cannot put $source to $opt->{ftp}->{put}->{www}.$fn ",
			  $ftp->message;
		}
	}
	$ftp->quit;
}

sub dir_walk {
	my ( $top, $filefunc, $dirfunc ) = @_;
	my $DIR;

	if ( -d $top ) {
		my $file;
		unless ( opendir $DIR, $top ) {
			warn "Couldn't open directory top: $!; skipping.\n";
			return;
		}

		my @results;
		while ( $file = readdir $DIR ) {
			next if $file eq '.' || $file eq '..';
			push @results, dir_walk( "$top/$file", $filefunc, $dirfunc );
		}
		return $dirfunc ? $dirfunc->( $top, @results ) : ();
	}
	elsif ( -f $top ) {
		return $filefunc ? $filefunc->($top) : ();
	}
}

sub is_in_array {
	my @elements;
	if ( ref $_[0] eq "ARRAY" ) { @elements = @{ $_[0] } }
	else                        { push @elements, $_[0] }

	my @array;
	if   ( ref $_[1] eq "ARRAY" ) { @array = @{ $_[1] } }
	else                          { @array = @_ }
	die join( ', ', @elements ), "\n", join( ', ', @array )
	  if ( join(@elements) =~ /git/ );
	foreach my $element (@elements) {
		return 1 if ( grep $_ eq $element, @array );
	}
	return undef;
}

Пример файла с настройками:

package UploadConfig;
use strict;
#use vars qw(@ISA @EXPORT);
#use Exporter;
use base 'Exporter';
 
our @EXPORT=qw($opt);
our $opt = {
    project        => "site",
    repo           => "c:/sn/xampp/htdocs/site/.git",
    tempdir        => "./temp",
    uploadfilesdir => "./upload",
    ftp            => {
        'server'   => 'ftp.***.ru',
        'login'    => "ftp",
        'password' => "***",
        'put'      => {
            'cgi' => "/site.ru/cgi/",
            'www' => "/site.ru/docs/",
        }
    },
    zipfiles => {

        "cgi.zip"  => "cgi-bin",
        "docs.zip" => [ "images", "js", "cms", "css", "txt", "." ],	#!"." - только файлы из корня
        "db.zip"   => "db.sql",
    },
    lastCommitFile => ".lastcommit",
    toChange       => {
        "main.pl" => {
            qr{
#\[~\?uselib\].*#\[~!uselib\]}si => qq{
#[~?uselib]
my \$serverpath="/home/site/site.ru/";
use lib "/home/site/site.ru/cgi/fuc";
use lib "/home/site/site.ru/cgi/lib";
use lib "/home/site/site.ru/cgi";
#[~!uselib]},
            "#!perl\.exe" => "#!/usr/bin/perl"
        },
        excludeFiles => ["MyConfig.pm", ".git"],
    }
};
Анастасия

Создание и синхронизация удаленной ветки в git

Надо добавить удаленную ветку, чтобы она синхронизировалась через pull/push.
Сначала надо создать удаленную ветку:
git push origin origin:refs/heads/newbranch

притянуть ее
git pull origin

посмотреть появилась ли в списке удаленных веток:
git branch -r

создать локальную ветку и закрепить ее за удаленной
git checkout --track -b newbranch origin/newbranch

На других рабочих компьютерах надо посмотреть удаленные ветки
git branch -r

и сделать checkout
git checkout --track -b newbranch origin/newbranch


Удалить удаленную ветку можно так
git push origin :heads/newbranch

 

Теги: git
Анастасия

Команды git

это краткое содержание часто используемых команд.

git init - новый репозиторий
git status - состояние: что было редактировано, что добавлено в индекс для коммита

git add . - добавить в индекс все изменения
git add file.txt - добавить содержимое файла в индекс
git add -i - интерактивное добавление позволяет выбирать файлы, которые надо добавить. Для каждого файоа можно добавить некоторые изменения, другие оставить для следующего коммита.
git add -p - про каждое изменение в файле спрашивает, добавить его в индекс или нет -p==patch

git commit -m "initial commit" - первый commit (некоторые команды криво работают, пока не сделать первый коммит)
git commit -am "comment" - add + commit, но новые файлы не будут добавлены
git commit --amend - докоммитить предыдущий коммит. если что еще надо в него добавить или изменить текст коммита:
git commit --amend -m "new comment"
git commit --amend -C HEAD - не менять текст коммита. Вместо HEAD может быть любая ссылка на коммит, например:
- HEAD - последний коммит текущей ветки
- sha1_hash коммита
- имя ветки
- имя_ветки~2 - два коммита назад от HEAD'a ветки
- тег
- HEAD^2 - два коммита назад от HEAD
- HEAD~1 - тоже два коммита назад от HEAD

git log - логи коммитов
git log --all - все ветки
git log --graph --all --oneline - все ветки, показать связь между ветками. информация об одном коммите должна влезать на одну строку
git log -p - патчи

git branch - список веток
git branch newfeature - создать ветку newfeature
git checkout newfeature
или тоже самое одной командой:
git checkout -b newfeature
...редактирование (программирование фичи)
git commit -am "new feature done" - ее commit
git checkout master - переход в ветку master
git merge newfeature - объединение ветки newfeature с master
теперь в master работает новая фича.
Если при слиянии возникли конфликты, то посмотреть на них можно:
git status
edit Press.pm - поправить
git add Press.pm
git commit - завершает процесс слияния
Если надоело разрешать конфликты, то можно вернуть обе ветки в исходное состояние:
git reset --hard HEAD
А если commit завершен, то вернуть обратно можно так:
git reset --hard ORIG_HEAD

git checkout -f - отметить незакоммиченные изменения

git checkout -- file.txt - вынимает версию файла, подготовленную к коммиту (после git add file.txt)
git checkout <commit> file.txt - из указанного коммита

git tag v4.2 - простой тег
git tag -a v4.2 -m "stable v4.2" - аннотированный тег (более подробный, вроде commit'a)
git tag -d v4.2 - удалить
git tag - список тегов
git tag -l <pattern> - поиск по шаблону
git log v4.2 - все коммиты, начиная с тега v4.2
git log v4.2 Press.pm - все коммиты, начиная с тега v4.2, в включающие изменения файла Press.pm
git branch --contains v4.2 - ветки с этой меткой
git show v4.2 - на какой commit указывает тег, когда и кем был сделан.
git describe - показывает сколько коммитов было с последнего тега:
v4.2-g[хэш последнего коммита]

git reset - undo
git reset --hard <commit> - отменить самым радикальным и не поправимым образом все, что было сделано и закоммичено после коммита <commit>. Рабочая папка тоже будет содержать проект из этого коммита.
git reset --soft <commit> - делает <commit> последним коммитом (HEAD'ом). Рабочая папка остается не тронутой.

git giu - оконный интерфейс в windows
gitk - оконный интерфейс в linux
gitk --all - все ветки

git update-index - обновить индекс содержимого файлов
git update-index --add - добавлять файлы с диска
git update-index --remove - удалять файлы, если их нет на диске

git diff - все изменения от последнего commit'a
git diff file.txt - изменения в одном файле
git diff --cached - сравнение последнего commit'a и того, что подготовлено для нового коммита
git diff <commit> - сравнение commit'a и рабочей папки, например
git diff HEAD^2 file.txt
git diff --stat sha1old..sha2new - список файлов и количество измененных строк между двумя коммитами
Сравнение двух веток:
git diff master..newfeature - сравнение двух последних коммитов веток
git diff master...newfeature - показывает разницу между последним коммитом в newfeature и его последнего предка в master. Полезно, если ветка newfeature была объединена с master, а потом в ней еще что-то изменилось. Тогда эта команда покажет разницу с последнего merga.

git blame file.txt - кто и когда редактировал файл

Временные коммиты
git stash - сделать временный коммит рабочей папки
... поправить что-нибудь в другой ветке, переключиться обратно и
git stash apply - вернуть рабочую папку обратно.
git status list - выводит список стешей, их может быть много, например:

git stash save "comment"
git stash apply stash@{1} - где stash@{1} - нужный стеш из git status list

Файл .gitignore - список шаблонов файлов, история по которым не ведется. То есть файлы, которые не попадают в репозиторий Например:
#пароли к бд
MyConfig.pm
#temp files:
*.*~
#все логи, кроме ошибок
*.log
!error.log

Или можно запретить все, кроме избранных файлов:
*.*
!*.pl
!*.pm

Удаленные repo:
git branch -a - список веток, включая удаленные (т.е. с исходного репозитория)
git remote add remoteName remoteURL - добавить удаленную ветку.
Все удаленные репозитории показываются в файле .git/config
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/* - связи между ветками
url = http://sn:@192.168.0.2/project/projectname.git

Забрать изменения с удаленного компьютера можно двумя способами:
1)
git fetch origin master - забирает с удаленного компьютера изменения в ветке master
git diff master..origin/master - посмотреть, что изменилось
git checkout -b testbranch
и
git merge origin/master - объединить изменения с удаленного компьютера с веткой master
...протестировать
git checkout master
git merge testbranch
2) все это одной командой (если не надо тестировать):
git pull origin master

git push origin master - пихает изменения из текущей ветки в origin master. Желательно делать push только в bare репозитории, т.к. команда push портит содержимое рабочей папки.

git rebase master - на master накладывается текущая ветка (сначала будут идти все коммиты master, потом все коммиты текущей ветки). Если просто объединить, то коммиты будут перемешаны и получится не красиво.
если возникли конфликты, их надо разрешить и запустить
git rebase --continue
Если надоест:
git rebase --abort - отменить все
git rebase --skip - пропустить один коммит

git rebase -i HEAD~10 - почистить историю последних 10 коммитов
в текстовом редакторе (из переменной окружения EDITOR) откроется список коммитов в формате:
pick hash message
Чтобы удалить коммит - удалить строку,
Поменять порядок коммитов - поменять порядок строк
объединить коммит с предыдущим - pick поменять на squash
для внесения изменений в коммит - pick на edit
После этого, если отметили коммиты, то
git commit --ament
если нет, то
git commit --continue
Подробности в git help rebase

git filter-branch --tree-filter 'rm cgi-bin/MyConfig.pm' HEAD Удалить файлы из всех коммитов
подробности в git help filter-branch

поиск коммита, в котором возникла ошибка:
git bisect start
git bisect bad sha1_bad_commit
git bisect good sha1_good_commit
git извлечет состояние по середине между хорошей и плохой версиями.
Если в этом состоянии по прежнему есть ошибка:
git bisect bad
Если работает, то
git bisect good
После окончания изменения надо выполнить
git bisect reset
Можно автоматизировать процесс тестирования командой:
git bisect run "perl testrun.pl"
Скрипт testrun.pl должен возвращать 0, если все хорошо.

импорт-экспорт истории:
git fast-import </tmp/history
git fast-export >/tmp/history

submodule
Позволяет добавлять посторонние репозитории в отдельную папку проекта.
Подробности git help submodule
Недостатки:
1. Команда git archive игнорирует субмодули
2. в рабочей копии после git checkout надо выполнять
git submodule update --init

subtree
История коммитов из внешнего проекта перенаправляется в подпапку с использованием стандартного механизма работы с внешними ветками.
git remote add -f creocms /path/to/creocms
git merge -s ours --no-commit creocms/master
git read-tree --prefix=creocms/ -u creocms/master
git commit -m "Merge creocms"
Изменения из creocms вытягиваются командой:
git pull -s subtree creocms master
В каждой рабочей копии надо выполнять
git remote add -f creocmms /path/to/creocms
И в каждой новой ветке:
git fetch creocms

 

Литература:
1. Getting goot with git by Andrew Burgess
2. progit.org/book by Scott Chacon
3. Git Magic by Ben Lynn
3. из командной строки: git help команда

 

find . -name .DS_Store -print0 | xargs -0 git rm -f --ignore-unmatch - удалить файлы .DS_Store из репозитория и с диска
Теги: git
Анастасия

Перенос репозитория из рабочей папки windows на linux-сервер по-быстрому

На рабочем компьютере в windows есть проект в папке z:/home/project/ (под denver'ом). Тут же в папке .git/ храниться репозиторий. Надо залить его на локальный сервер под linux. Для этого надо сделать bare-репозиторий:

>mkdir z:/repo
>cd z:/repo
>git clone --bare --no-hardlinks z:/home/project/.git project.git
скопировать его, например по ftp, на сервер в папку /home/httpd/repo/project.git. И выполнить:
$ git --bare update-server-info

Теперь можно клонировать на любом компьютере в локальной сети:
git clone ssh://login:password@linux_server/home/httpd/repo/project.git
или вот так с подробностями
git clone -v ssh://login:password@linux_server/home/httpd/repo/project.git

А рабочем компьютере в windows в папке z:/home/project/.git правим config:
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = ssh://login:password@linux_serveer/home/httpd/repo/project.git
что-нибудь редактируем commit & push на сервер :)

Другие варианты:
http://toroid.org/ams/git-website-howto
https://github.com/ezyang/git-ftp
http://www.jedi.be/blog/2009/05/06/8-ways-to-share-your-git-repository/
http://www.kernel.org/pub/software/scm/git/docs/git-bundle.html

Например, можно через git deamon вместо ssh. Демон запускается так
git daemon --base-path=/home/httpd/repo
Клонируется так:
git clone git://linux_server/home/httpd/repo/project.git

 

Теги: git
© 2002-2016 Креограф. Все права защищены законом РФ
 Русский /  English