Merge two folder (update file no overwrite), command line - bash

I've got two folder: provided and done. At start done is made by copying provided and then i've made some changes in done (implementing some functions). Then comes an update in provided: some functions in existing files are added and there is some new files too
I want to merge theses two folder (provided into done):
new files must by copied
existing files must be updated (as in a git merge, appending only what is new) -- this is the hard part
Is there any existing command (for linux) that can achieve this?

git merge-file current-file base-file other-file
git merge-file incorporates all changes that lead from the base-file to other-file into current-file. The result ordinarily goes into current-file.

Related

Git smudge and clean using local configuration branch

The local configuration of the project I'm working on involves changing several files in complicated ways that cannot be committed to any submitted branches. To work around this I've committed these local configuration changes to a dedicated local branch config, and have been running this bash script config.sh after starting a new work branch:
#!/bin/bash
# put relevant config files in array
mapfile -t files < <(git diff config develop --name-only)
# overwrite only those files to my working directory
git checkout config -- ${files[#]}
# unstage them so they aren't accidentally committed
git reset HEAD ${files[#]}
echo The following files were successfully overwritten for local configuration:
printf '\t%s\n' "${files[#]}"
Along with another .deconfig script that does the same in reverse. Run directly from the terminal, these scripts have been working fine, but I'd like to streamline the process further using git's clean and smudge filters. So I created a .gitattributes file:
*.* filter=config
and then added this to my .git/config file:
[filter "config"]
smudge = ./config.sh
clean = ./deconfig.sh
However, it just isn't working. If I had to guess it's because git isn't expecting me to run an additional checkout as part of a filter, which itself runs after the checkout command against all files. Most use cases for smudge and clean seem to involve simple find and replace operations, but that approach would be complicated to implement and difficult to maintain given the complexity of changes needed. I could store the configuration files in a static, external directory somewhere, but I'd like to smudge and clean based off the same configuration branch because the local configuration itself frequently evolves and benefits from versioning alongside the rest of the project, and ideally the branch could be used as a baseline for other devs for their local configuration. Git's filter-branch might be a better fit but git's own documentation recommends against using it at all. Is there a way to do this? Is there something wrong with my git configuration? Could the script itself be causing a problem? Any other possible approaches?
Although it is not documented anywhere, you cannot change the state of the working tree with a smudge or clean filter. Git expects to invoke the filter once for each file by piping data into it and reading the data from the standard output. In other words, these filters are intended to be invoked on a per-file basis and process only that file, not by modifying the working tree state.
The best solution to your problem is to avoid keeping a separate branch. Simply keep all of the files, both development and production, in some directory, and use a script to copy the correct one into place. The location of the running config file should be ignored, so the script won't cause Git to show anything as modified. Alternatively, keep a template somewhere, and have the script generate the appropriate one based on the environment. This is good if you have secrets for production that should not be checked in; you can pass them to the script through the environment and have the right values generated.
What you're doing is related to ignoring tracked files, which, as outlined in the Git FAQ, generally can't be done successfully.

Git: Reconcile Two Folders with Duplicate Name (Cased Different) in Windows While Preserving History

I recently discovered that there are a couple folders in my solution that have two distinct paths in Git (GitHub shows two separate folders), one being FooBar and the other being Foobar. This is because some files were registered with the former folder name as their path, and some with the latter.
This was discovered locally (in Windows) by configuring Git to not ignore case: git config core.ignorecase false
I took a stab at fixing this by deleting the whole folder, committing, then re-adding the folder and committing again. This fixed the problem, but the files that got their paths changed lost their Git History. Running gitk against the new path for these files showed just the one commit. Running gitk against their old path revealed their whole history.
Next stab: Use git mv to move the file:
git mv Foobar/file.txt FooBar/file.txt
This yields the error:
fatal: destination exists, source=Foobar/file.txt, destination=FooBar/file.txt
And if I try deleting the file first, of course Git complains that the source file doesn't exist.
Then I discovered Git doesn't complain about the destination already existing if you add -f to the mv command. However, after committing that rename, gitk shows that the history got severed anyway!
I even attempted to do the three step dance described here but this was just another way of doing the -f. Same result.
Basically I just want to move a file from Foobar/file.txt to FooBar/file.txt in a case-insensitive operating system in some way, while preserving Git history. Is this possible?
There is no simple solution to the real problem.
In Git, files don't have history. Commits have history—or more precisely, commits are the history. That is all the history there is. For Git to "follow" a file, as in git log --follow <path>, Git looks at the commits, one at a time, comparing each commit to its parent commit.
If a diff between parent and child shows that the parent contains a file named parent/path/to/pfile and the child contains a file named child/path/to/cfile and the content of these two files, in these two commits, is "sufficiently similar" (several conditions must hold here), then, in Git's "eyes", that parent-to-child transition represents a rename of that file. So at that point, git log --follow, which had been looking for child/path/to/cfile, starts looking instead for parent/path/to/pfile.
Without --follow, git log does not do this special "find a rename" operation ... and in general, Git believes that any path names with any byte-level difference represent different files. In other words, case-folding and UTF-8 normalization do not occur. Consider, e.g., the word schön, which can be represented as either s c h ö n or s c h o combining-¨ n. We can, on a Linux box, create two different files using these two different UTF-8 style names. Running ls will show two files whose name appears the same:
$ cat umlaut.py
import os
p1 = u'sch\N{latin small letter o with diaeresis}n'
p2 = u'scho\N{combining diaeresis}n'
os.close(os.open(p1.encode('utf8'), os.O_CREAT, 0o666))
os.close(os.open(p2.encode('utf8'), os.O_CREAT, 0o666))
$ python umlaut.py
$ ls
schön schön umlaut.py
Git is perfectly happy to store both files, separately. However, MacOS refuses to allow both files to coexist, in the same way that Windows—and for that matter, MacOS by default as well—refuses to allow both Foobar and FooBar to coexist.
Make Git store the file in new commits under the new byte-sequence, and history is preserved, it's just not the history you want preserved. But the history that's already in the repository is already not the history you want preserved.
In practice, you should probably just rename the file in Git's eyes—which has no effect on the file's name in your OS's eyes; FooBar and Foobar are the same name here—and get on with things. Your alternative is to rewrite all history going back in time to the point at which the bad pairings were first added to the repository, by copying (with slight modifications) each "bad" commit to a new-and-improved "good" commit. But this then means getting everyone who uses the repo to switch from "bad old repo" to "new and improved good repo".

Programmatically overwrite a specific local file with remote file on every git pull

I have an XML file that we consider binary in git. This file is externally modified and committed.
I don't care about who edited it and what's new in the file. I just want to have the latest file version at every pull. At this time, at every git pull I have a merge conflict.
I just want that this file is overwritten on every git pull, without manually doing stuff like git fetch/checkout/reset every time I have to sync my repo.
Careful: I want to overwrite just that file, not every file.
Thanks
I thought you could use Git Hooks, but I don't see one running before a pull...
A possible workaround would be to make a script to delete this file and chain with the needed git pull...
This answer shows how to always select the local version for conflicted merges on a specific file. However, midway through the answer, the author describes also how to always use the remote version.
Essentially, you have to use git attributes to specify a specific merge driver for that specific file, with:
echo binaryfile.xml merge=keepTheirs > dir/with/binary/file/.gitattributes
git config merge.keepTheirs.name "always keep their file during merge"
git config merge.keepTheirs.driver "keepTheirs.sh %O %A %B"
git add -A
git commit -m "commit file for git attributes"
and then create keepTheirs.sh in your $PATH:
cp -f "$3" "$2"
exit 0
Please refer to that answer for a detailed explanation.
If the changes to your files are not actual changes, you should not submit them. This will clutter your version history and cause numerous problems.
From your statement I’m not quite sure which is the case, but there are 2 possibilities:
The file in question is a local storage file, the contents of which are not relevant for your actual sourcecode. In this case the file should be part of your .gitignore.
This file is actually part of your source and will thus have relevant changes in the future. By setting up the merge settings like you are planning to do, you will cause trouble once this file actually changes. Because merges will then be destructive.
In this case the solution is a little bit more complicated (apart from getting a fix for the crappy tool that changes stuff it doesn’t actually change …). What you are probably looking for is the assume unchanged functionality of git. You can access it with this command:
git update-index --assume-unchanged <file>
git docu (git help update-index):
You can set "assume unchanged" bit to
paths you have not changed to cause git not to do this check. Note that setting this bit on a path does not mean git will check the
contents of the file to see if it has changed — it makes git to omit any checking and assume it has not changed. When you make changes
to working tree files, you have to explicitly tell git about it by dropping "assume unchanged" bit, either before or after you modify
them.

Incremental deploy from a shell script

I have a project, where I'm forced to use ftp as a means of deploying the files to the live server.
I'm developing on linux, so I hacked together a bash script that makes a backup of the ftp server's contents,
deletes all the files on the ftp, and uploads all the fresh files from the mercurial repository.
(and taking care of user uploaded files and folders, and making post-deploy changes, etc)
It's working well, but the project is starting to get big enough to make the deployment process too long.
I'd like to modify the script to look up which files have changed, and only deploy the modified files. (the backup is fine atm as it is)
I'm using mercurial as a VCS, so my idea is to somehow request the changed files between two revisions from it, iterate over the changed files,
and upload each modified file, and delete each removed file.
I can use hg log -vr rev1:rev2, and from the output, I can carve out the changed files with grep/sed/etc.
Two problems:
I have heard the horror stories that parsing the output of ls leads to insanity, so my guess is that the same applies to here,
if I try to parse the output of hg log, the variables will undergo word-splitting, and all kinds of transformations.
hg log doesn't tell me a file is modified/added/deleted. Differentiating between modified and deleted files would be the least.
So, what would be the correct way to do this? I'm using yafc as an ftp client, in case it's needed, but willing to switch.
You could use a custom style that does the parsing for you.
hg log --rev rev1:rev2 --style mystyle
Then pipe it to sort -u to get a unique list of files. The file "mystyle" would look like this:
changeset = '{file_mods}{file_adds}\n'
file_mod = '{file_mod}\n'
file_add = '{file_add}\n'
The mods and adds templates are files modified or added. There is a similar file_dels and file_del template for deleted files.
Alternatively, you could use hg status -ma --rev rev1-1:rev2 which adds an M or an A before modified/added files. You need to pass a different revision range, one less than rev1, as it is the status since that "baseline". Deleted files are similar - you need the -d flag and a D is added before each deleted file.

Change case of a file on Windows? [duplicate]

This question already has answers here:
How to make git ignore changes in case?
(6 answers)
Closed 10 months ago.
There are a couple of files in our git-controlled codebase that I'd like to rename. Specifically, I just want to change the case of the file, so that sourceCode.java becomes SourceCode.java, for example. The catch: I'm on a Windows box, and the filesystem thinks those are the same file name.
How can I get Windows and Git to recognize that change and check it in?
The change of the file name should not be ignored, but committed to git.
To rename the file you can use the standard git mv command.
Since Windows treats files with only changes in case as identical, you have to pass the -f option to force a rename:
git mv -f name.java Name.java
If instead you want to ignore case changes, have a look at the question
How to make git ignore changes in case?.
If you are on a FAT file system your only choice is to do a two stage rename:
Rename sourceCode.java to anything.you.like
Rename anything.you.like to SourceCode.java
Back in the days when we used Perforce we had exactly this problem and this was the only solution we could come up with.
The following steps allowed me to change the case on Windows:
Add ignorecase = false to [core] in .git/config;
Move the files you are going to rename out of your project directory;
Add the deletes to the index;
Move all files back to their original location and change the case of the files and/or directories;
Add all "new" files to the index;
Remove ignorecase = false added at the first step.
This way you have a single commit that contains the rename and it makes it easy to change e.g. an entire directory.
In my opinion one simple way is missing. You can do this for a single file, a specific directory or even the whole repository. Just rename your files in any way you like before and than execute these commands:
git rm --cached <file name or directory>
git add <file name or directory>
If you want to affect also the sub-directories, you have to use the -r flag:
git rm -r --cached <directory>
git add <directory>
Be careful. Doing this can lead to changes that are impossible to merge. Git gets confused when merging on Windows because it can't decide whether the old Uppercase name and the new lowercase name are the same file or not (for Git they are not, but for the filesystem they are). To merge you have to do some manual workaround like deleting the files before merging.
See Git rebase issue with files of same name but different case
I'm not sure if this issue is worse than having an unconventionally-named file in your project for ever and ever, but it is worth knowing about if there are lots of users with lots of branches which will all need to be merged eventually.
With NTFS (or FAT), a single git mv command does not solve the problem.
This question shows a technique that works:
git mv and only change case of directory

Resources