The next task is: I need to create a repository, where there will be different script files (for example, A). There will also be a repository, where there will be a standard set of files for starting the site programming (for example, B). When I create a new project, a new repository is created (for example, C).

Now I need to transfer the standard set from C to C in. Then I need to transfer 2 scripts from A to C. After that, I see that there is an error in the standard files (which are stored in B). I correct it and I want to pour it both on B and S. And so on. That is, I need to work with them at the same time.

Already tried git and svn, nothing happened. The solution through git submodule is not suitable , because common files need to be edited in specific projects, while still being able to pull up changes in the same files from the main set.

In which version control system and how can you organize storage for this task?

  • 2
    Why not simplify the scheme, and not merge A + B into one repository? In general, git is preferable, git submodule to help. - Vitali Falileev
  • the task is to use many repositories at the same time - Diefair
  • git submodule is a bit wrong, it will create a separate directory, and I just need to clone the file and so that it already participates in the main project - Diefair
  • so you are doing git clone <repo-b> project-c ? - Vitali Falileev
  • Yes, I clone the main project, then I want to upload files from another one, and I want to be able to upload files to both the first and second - Diefair

2 answers 2

Important: if your language has a good dependency management system, then the solution given here is better not to use. Because if it is, it is much easier to make a separate library from the base and install it as a dependency . In Node.js, the NPM package, in Ruby — gem, in PHP — Composer, in Rust — crate, and so on.

Otherwise, there will be redundancy and troubles may be added with updates that can be avoided by initially not using the version control system for other purposes.

git can do that.

In general, I have the impression that git can do anything. True, the mechanism of work is not too simple, you need to understand how it will work.

Since git by its nature distributed, I will emulate your workflow in one local repository on several unrelated branches : a , b and master . Changes in these branches can easily appear from other repositories (different branches can monitor different servers!), But when using this technique you should locally have a repository that has all three.

Let's go point by point:

Suppose that you did git init , and C (the project itself) is in the master branch (which is completely optional).

create a repository with different script files (for example, A)

Make an "unrelated branch":

 git checkout --orphan a 

We clean the folder and index to start with a clean slate:

 git reset && git clean -f 

Making a commit with garbage:

 echo 'скрипт' | tee script_a_1 script_a_2 git add . git commit # Тут вас попросят ввести сообщение для коммита 

the repository, where there will be a standard set of files to start programming the site (for example, B)

Same.

 git checkout --orphan b git reset && git clean -f echo 'скрипт' | tee script_b_1 script_b_2 git add . git commit # Сообщение для коммита 

When I create a new project, a new repository is created (for example, C).

We assume that this is the master branch. And at the moment it should be empty, for git it means that it does not exist, so you have to do it again:

 git checkout --orphan master git reset && git clean -f 

Now I need to transfer the standard set from C to C in

Murge:

 git merge b 

When this happens, fast-forward to b , master will match branch b . This is normal. Indeed, at this stage, the state of the project file system is the same, right?

Then I need to transfer 2 scripts to A from C.

Murge with a , but this time with “brakes”, so that git would stop right before the commit:

 git merge --no-commit --no-ff a 

What for? Then, that you do not need all the files. At this stage, you can remove unnecessary files from the index from the index with the help of reset , clean up the remaining and commit the result:

 git reset script_a_2 git clean -f git commit 

Now let's "work a little" for the form:

 echo work > work git add work git commit 

There is an error in standard files (which are stored in B). I am correcting her and I want to pour both on ...

Since you have (semantically) master based on b , and not vice versa, you need to fix the error in b in order for the changes to "spread" (not automatically!) For those who use it. Go to branch b and repair:

 git checkout b # clean уже не нужен, т. к. ветка не пустая echo script > script_b_1 # Ну, допустим, что кириллица не переварилась. Мало ли. git add script_b_1 git commit 

At this stage, if the repositories with b and master still different, there should be git push branches b into the corresponding repository, and in the project repository you need to do git pull --ff-only ( --ff-only allows only winding the branch - to Your changes are not "bogged down" in b ) in the branch that is following the repository. This is a separate topic, if you're interested, I'll tell you about it.

... so in C.

Go to the branch with the project:

 git checkout master 

And we make merge branches b to the project.

 git merge b 

Is done. Yes, just like that!

After doing the above described manipulations, I received the following scheme from commits ( git log --graph ) in master :

 * commit 2e9219cb2922f70382a8f069fdc917bdbfccd4b8 |\ Merge: 2cc463a 1fc0b61 | | Author: Имя <адрес> | | Date: Sun Dec 27 14:46:12 2015 +0300 | | | | мердж фикса | | | * commit 1fc0b61c1017acf4b4ef1941e1cdcb01a7e86be4 | | Author: Имя <адрес> | | Date: Sun Dec 27 14:41:23 2015 +0300 | | | | фикс основы | | * | commit 2cc463ae97500bff4a304990aa4e42159da96fe9 | | Author: Имя <адрес> | | Date: Sun Dec 27 14:38:37 2015 +0300 | | | | работа | | * | commit cfa122c954f77b3282f51511f94bc6d97c95d569 |\ \ Merge: 7da066c f9304d8 | |/ Author: Имя <адрес> |/| Date: Sun Dec 27 14:31:20 2015 +0300 | | | | мердж с удалением лишних | | | * commit f9304d82d89600add270544bec32cd6661a8c150 | Author: Имя <адрес> | Date: Sun Dec 27 13:46:39 2015 +0300 | | скриптики | * commit 7da066c234216057ff19775a355342ed501fe9a4 Author: Имя <адрес> Date: Sun Dec 27 13:54:01 2015 +0300 основа 

And for clarity, I redraw:

enter image description here

  • one
    Thanks, but I thought it could be made easier ( - Diefair
  • one
    @Diefair it looks difficult, because git is pretty funny with empty branches and I have to do additional actions. In practice, it is 90% "push to the helpers", "pool in the project", "merge helpers to the project". Everything. - D-side
  • @Diefair I added a more illustrative repository pattern. Maybe it will help solve related issues with Git. - D-side
  • This solution is technically very cool, allows you to climb into the very wilds of the gita and return as a winner. But why not use submodules, are they easier? ) - Nick Volynkin
  • one
    @NickVolynkin I am not a front end vendor either, but in my opinion, any normal system of modules for a language makes this solution of little use, since details that can change over time can be packaged into a separate library and upgraded as a separate dependency if necessary (Rails copes with it, for example). That is, even without submodules. And the orphaned branches are only for demonstration, it is expected that in practice these will be separate repositories. Although the example may be quite good. - D-side

If you don’t want to make several branches that monitor different servers, and so on, as in another answer , then you can work in the same way as large distributed projects like the Linux kernel or Go are developed through patch files, with the only difference that nobody needs anything send by mail.

  1. Make changes to the C project files that need to be moved back to A or B :

     git add -p; git commit 
  2. We have a commit in which the files that occur only in A or B change, not necessarily at the same time. Check it with git show .
  3. Export this commit to a file:

     git format-patch -1 

    The command creates a file with changes like 0001-example.patch .

  4. Go to project A or B , apply the previously received patch there:

     git am ../ProjectC/0001-example.patch 
  5. At best, nothing else needs to be done.
  6. In the worst case, it may be necessary to resolve the usual conflicts that arise during a merger. For example :

     find . -type f -name \*orig -print -delete find . -type f -name \*rej -print -delete patch -p1 < .git/rebase-apply/patch find . -name \*.rej | while read rej do echo $rej wiggle -r ${rej%.rej} $rej done 

In the same way, changes are transferred back. That's all.

The restriction remains the same: for convenient and simple tracking of the history, files in project C should remain in the same place as in projects A and B.

The same applies to working with several projects: in order to transfer some change from the project-template B to copies C1 , C2 , C3 ... With any approach you will need to write a program or script.

Disadvantages of this approach

The drawbacks are obvious - this is a relatively manual job for transferring changes (which, in principle, can be automated), but you should not forget about the merits.

The merits of this approach

The most obvious advantage of this approach is simplicity in use and no need to remember where is which branch, which means less chance of doing the wrong thing. Less mistakes - less time to fix them. More time for more interesting activities.

Another advantage: confidentiality. If you ever need to give access to Git to outsiders (for example, your contractor to do routine work), then the outsider will not see the whole-all history of changes in projects A and B , which will definitely fall into C during any merging of branches. For example, if you start a new project from a template like this:

 # начинаем новый проект git clone TemlateB NewProjectC cd NewProjectC git reset $(echo "NewProjectC Started" | git commit-tree HEAD^{tree}) git remote rm origin # спустя некоторое время git am ../TemlateB/0001-example-fix.patch 

Then in such a situation in Project C, an outsider will see only some of the most recent commit and fixes, and not the entire history of projects A and B for all the time. Naturally, the outsider will not see any changes from other projects similar to C.

Scalability is another advantage of this approach. Maintain 30-50-80 copies of the project? No problems! Delegate work on a project to a contractor? Easy peasy! Remove any project in the archive out of sight? No problems! Return from the archive? Easy!

Now imagine how many screens will occupy 80+ branches for each of the projects ever launched. Even it is not worth talking about removing some branch to the archive or delegating something to someone. No, you can not say that it is impossible ...

  • There is no need to remember where is which branch - this is solved by creating for yourself some naming rules for branches, in the manner of those that use Git Flow . Still, in order to smergit a branch, you will have to enter and see its name. But the decision is also a good one, yes. - D-side
  • All right, but why solve a problem that can not be created initially? .. - sanmai
  • To solve other problems that already exist, of course :) - D-side
  • One or the other project according to the scheme with branches to support is seeds. Problems will arise when you have dozens of projects with a separate branch for each :) - sanmai
  • The variant on the branches imitates the order of work in the case when the project is in one repository, and its basis in another. On several branches it is only demonstrated. I can not imagine a realistic situation when the project is merjitsya from dozens of separate branches with different files. A bunch of projects on the branches was quite a common sight when the billing github went by the number of private repositories; git worktree add helps not to leave the coils in this order of work. - D-side